免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Spring框架簡介

原文地址:  http://my.oschina.net/myriads/blog/37922

1、使用框架的意義與Spring的主要內容

     隨著軟件結構的日益龐大,軟件模塊化趨勢出現(xiàn),軟件開發(fā)也需要多人合作,隨即分工出現(xiàn)。如何劃分模塊,如何定義接口方便分工成為軟件工程設計中越來越關注的問題。良好的模塊化具有以下優(yōu)勢:可擴展、易驗證、易維護、易分工、易理解、代碼復用。

     優(yōu)良的模塊設計往往遵守“低耦合高內聚”的原則。而“框架”是對開發(fā)中良好設計的總結,把設計中經常使用的代碼獨立出來,所形成的一種軟件工具。用戶遵守它的開發(fā)規(guī)則,就可以實現(xiàn)良好的模塊化,避免軟件開發(fā)中潛在的問題。廣義上的框架無處不再,一個常見的例子就是PC硬件體系結構,人們只要按照各自需要的主板、顯卡、內存等器件就可以任意組裝成自己想要的電腦。而做主板的廠商不用關心做顯卡廠商的怎么實現(xiàn)它的功能。軟件框架也是如此,開發(fā)人員只要在Spring框架中填充自己的業(yè)務邏輯就能完成一個模塊劃分清晰紛的系統(tǒng)。

     這里主要通過一個銀行通知用戶月收支記錄的小例子來介紹輕型J2EE框架Spring的主要內容、它所解決的問題和實現(xiàn)的方法。

Spring框架主要可以分為3個核心內容:

        1、容器

        2、控制反轉(IoC ,Inversion of Control)

        3、面向切面編程(AOP ,Aspect-Oriented Programming)

      例子中依次對這些特性進行介紹,描述了軟件模塊化后存在的依賴與問題,以及Spring框架如何解決這些問題。 

2、一個簡單的例子程序

     假設有一個如下應用場景:(1)一個銀行在每月的月初都需要向客戶發(fā)送上個月的賬單,賬單發(fā)送的方式可以為紙質郵寄、或者短信方式。(2)還有一個潛在的需求:為了安全起見,在每個函數(shù)操作過程中都需要記錄日志,記錄參數(shù)傳入是否正常,函數(shù)是否正常結束,以便出錯時系統(tǒng)管理員查賬。

     那么對這個需求進行簡單實現(xiàn)。系統(tǒng)框圖如下所示:

    首先定義一個賬單輸出的接口:

1//接口
2public interface ReportGenerator{
3    publicvoidgenerate(String[][] table) ;
4}

     實現(xiàn)“打印紙質賬單”與“發(fā)送短信”兩個具體功能:

1//賬單報表實現(xiàn)類 
2public class PageReportGenerator implement ReportGenerator {
3    publicvoidgenerate(String[][] table) {
4        log4j.info( ... );   //輸出日志 
5        ...打印操作,以便工作人員郵遞給客戶
6        log4j.info( ... );   //輸出日志 
7    
8}
1//短信報表實現(xiàn)類 
2public class SMSReportGenerator implement ReportGenerator {
3    publicvoidgenerate(String[][] table) {
4        log4j.info( ... );
5        ...短信發(fā)送操作
6        log4j.info( ... );
7     }
8}

    上層業(yè)務邏輯對上個月的賬目進行統(tǒng)計并調用接口產生紙質或者短信結果:

01//上層業(yè)務中的服務類 
02public class ReportService{ 
03    privateReportGenerator reportGenerator =newSMSReportGenerator(); 
04    publicvoidgenerateMonthlyReport(intyear,int month) { 
05        log4j.info( ... ); 
06        String[][] statistics =null
07        ... 
08        reportGenerator.generate(statistics); 
09    }
10
這個實現(xiàn)源代碼請查看文章結尾附錄中的"BankOld"。源代碼中與例子中程序略有區(qū)別:由于使用log4j需要引用外部的包,并且需要寫配置文件,為了方便,源代碼中的日志輸出用system.out.println()代替。

3、Spring中的容器

        A、模塊化后出現(xiàn)的問題與隱患

        假設隨著工程的復雜化,上面的例子需要分成兩個模塊,以便開發(fā)時分工,一般會以如下結構劃分:

    劃分后再看原來的代碼: 

1//上層業(yè)務中的服務類 
2public class ReportService{ 
3    privateReportGenerator reportGenerator =newSMSReportGenerator();//隱患 
4  
5    publicvoidgenerateMonthlyReport(intyear,int month) { 
6        ... 
7    
8}

     在服務類有private ReportGenerator reportGenerator = new SMSReportGenerator();這么一行代碼,ReportService類與SMSReportGenerator類不屬于同一個模塊,當開發(fā)人員B對內部實現(xiàn)進行修改時,由于存在依賴,開發(fā)人員A也要進行修改(比如之前喜歡短信收賬單的客戶感覺短信不夠詳細,希望以后改用郵件收賬單,那么開發(fā)人員B需要實現(xiàn)一個MailReportGenerator類,在開發(fā)人員B修改代碼是,開發(fā)人員A也需要改代碼------聲明部分修改)。如果系統(tǒng)龐大new
SMSReportGenerator()大量使用的話,修改就會十分復雜,一個聲明沒有修改就會出現(xiàn)大的BUG。

    所以需要一種劃分,讓各個模塊盡可能獨立,當開發(fā)人員B修改自己的模塊時,開發(fā)人員A不需要修改任何代碼。

    B、問題出現(xiàn)的原因

    為例子中的程序畫一個UML依賴圖:

    可以發(fā)現(xiàn)上述問題出現(xiàn)的原因主要是:模塊A與模塊B不但存在接口依賴,還存在實現(xiàn)依賴。ReportGenerator每次修改它的實現(xiàn),都會對ReportService產生影響。那么需要重構消除這種實現(xiàn)依賴。

    C、用容器解決問題

    消除實現(xiàn)依賴一般可以通過添加一個容器類來解決。在例子程序容器代碼如下: 

01//容器類 
02public class Container { 
03  
04    publicstaticContainer instance; 
05  
06    privateMap<String, Object> components; 
07  
08    publicContainer(){ 
09        component =newHashMap<String, Object>(); 
10        instance =this
11  
12        ReportGenertor reportGenertor =newSMSReportGenertor(); 
13        components.put(“reportGenertor”, reportGenertor); 
14  
15        ReportService reportService =newReportService(); 
16        components.put(“reportService”, reportService); 
17    
18  
19    publicObject getComponent(String id){ 
20        returncomponents.get(id); 
21    
22}

    使用容器后,模塊A的ReportService的屬性實現(xiàn)方法也發(fā)生了變化。

01//服務類變更,降低了耦合 
02public class ReportService{ 
03  
04    //private ReportGenerator reportGenerator = new SMSReportGenerator(); 
05    privateReportGenerator reportGenerator = (ReportGenerator) Container.instance.getComponent(“reportGenerator”); 
06  
07    publicvoidgenerateMonthlyReport(intyear,int month) { 
08        ... 
09    
10}

     這樣的話,class都在容器中實現(xiàn),使用者只需要在容器中查找需要的實例,開發(fā)人員修改模塊B后(在模塊中增加郵件報表生成類MailReportGenerator),只需要在容器類中修改聲明(把ReportGenertor
reportGenertor = new SMSReportGenertor();改為ReportGenertor reportGenertor = new
MailReportGenertor();)即可,模塊A不需要修改任何代碼。一定程度上降低了模塊之間的耦合。

4、Spring中的控制反轉

 

    A、還存在的耦合

    使用容器后模塊A與模塊B之間的耦合減少了,但是通過UML依賴圖可以看出模塊A開始依賴于容器類:

    之前的模塊A對模塊B的實現(xiàn)依賴通過容器進行傳遞,在程序中用(ReportGenerator) Container.instance.getComponent(“reportGenerator”)的方法取得容器中SMSReportGenertor的實例,這種用字符(“reportGenerator”)指代具體實現(xiàn)類SMSReportGenertor 的方式并沒有完全的解決耦合。所以在銀行賬單的例子中我們需要消除ReportService對容器Container的依賴。

    B、控制反轉與依賴注入

    在我們常規(guī)的思維中,ReportService需要初始化它的屬性private ReportGenerator reportGenerator就必須進行主動搜索需要的外部資源。不使用容器時,它需要找到SMSReportGenertor()的構造函數(shù);當使用容器時需要知道SMSReportGenertor實例在容器中的命名。無論怎么封裝,這種主動查找外部資源的行為都必須知道如何獲得資源,也就是肯定存在一種或強或弱的依賴。那是否存在一種方式,讓ReportService不再主動初始化reportGenerator,被動的接受推送的資源?

    這種反轉資源獲取方向的思想被稱為控制反轉(IoC,Inversion of Control),使用控制反轉后,容器主動地將資源推送給需要資源的類(或稱為bean)ReportService,而ReportService需要做的只是用一種合適的方式接受資源??刂品崔D的具體實現(xiàn)過程用到了依賴注入(DI,Dependecncy Injection)的設計模式,ReportService類接受資源的方式有多種,其中一種就是在類中定義一個setter方法,讓容器將匹配的資源注入:setter的寫法如下: 

01//為需要依賴注入的類加入一種被稱為setter的方法 
02  
03public class ReportService{ 
04  
05    /*private ReportGenerator reportGenerator = 
06        (ReportGenerator) Container.instance.getComponent(“reportGenerator”); */ 
07  
08    privateReportGenerator reportGenerator; 
09  
10    publicvoidsetReportGenerator( ReportGenerator reportGenerator) { 
11        this.reportGenerator = reportGenerator; 
12    
13  
14    publicvoidgenerateMonthlyReport(intyear,int month) { 
15        ...  
16    
17}

    在容器中把依賴注入: 

01//容器類   
02public class Container { 
03       
04    ... 
05    publicContainer ( ) { 
06        component =newHashMap<String, Object>(); 
07        instance =this
08        ReportGenertor reportGenertor =newSMSReportGenertor(); 
09        components.put(“reportGenertor”, reportGenertor); 
10  
11        ReportService reportService =newReportService(); 
12        reportService.setReportGenerator(reportGenerator);//使用ReportService的setter方法注入依賴關系 
13        components.put(“reportService”, reportService);
14    
15    ... 
16}

    這樣一來ReportService就不用管SMSReportGenertor在容器中是什么名字,模塊A對于模塊B只有接口依賴,做到了松耦合。

    C、Spring IoC容器的XML配置

    每個使用Spring框架的工程都會用到容器與控制反轉,為了代碼復用,Spring把通用的代碼獨立出來形成了自己的IoC容器供開發(fā)者使用:

 

    與上面例子中實現(xiàn)的容器相比,Spring框架提供的IoC容器要遠遠復雜的多,但用戶不用關心Spring
IoC容器的代碼實現(xiàn),Spring提供了一種簡便的bean依賴關系配置方式------使用XML文件,在上面的例子中,配置依賴關系只要在工程根目錄下的“application.xml”編輯如下內容:

01<?xmlversion="1.0"encoding="UTF-8"?> 
02<beans   xmlns="http://www.springframework.org/schema/beans"
03    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
04    xmlns:p="http://www.springframework.org/schema/p"
05    xsi:schemaLocation="http://www.springframework.org/schema/beans
06         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" >
07  
08    <beanid="smsReportGenerator"class="bank.SMSReportGenerator"/>
09  
10    <beanid="reportService"class="bank.ReportService">  
11       <propertyname="reportGenerator"ref="smsReportGenerator"/> 
12    </bean>
13 </beans>

        <?xml version="1.0" encoding="UTF-8"?>是標準的XML頭,xmlns引用的是一些命名空間,兩個一般在工程中自動生成。后面的內容由用戶輸入,主要表示實例化SMSReportGenerator,實例化ReportService并把SMSReportGenerator的對象smsReportGenerator賦值給ReportService的屬性reportGenerator,完成依賴注入。

5、Spring中的面向切面編程

 

    A、日志問題以及延伸

    在例子的需求中有一條是:需要記錄日志,以便出錯時系統(tǒng)管理員查賬。回顧例子中的代碼,在每個方法中都加了日志操作: 

01//服務類 
02public class ReportService{ 
03    ... 
04    publicvoidgenerateMonthlyReport(intyear,int month) { 
05        log4j.info( ... );  //記錄函數(shù)的初始狀況參數(shù)等信息 
06        String[ ][ ] statistics =null
07        ... 
08        reportGenerator.generate(statistics); 
09        log4j.info( ... );  //記錄函數(shù)的執(zhí)行狀況與返回值 
10    
11}
01//憑條報表實現(xiàn)類   
02public class PageReportGenerator implement ReportGenerator { 
03  
04    publicvoidgenerate(String[ ][ ] table) { 
05  
06        log4j.info( ... );      //記錄函數(shù)的初始狀況參數(shù)等信息 
07        …打印操作 
08        log4j.info( ... );      //記錄函數(shù)的執(zhí)行狀況與返回值 
09    
10}

    可以看出在每個方法的開始與結尾都調用了日志輸出,這種零散的日志操作存在著一些隱患,會導致維護的困難。比如日志輸出的格式發(fā)送了變化,那么無論模塊A還是模塊B的程序員都要對每個方法每個輸出逐條修改,極容易遺漏,造成日志輸出風格的不一致。又比如不用Log4j日志輸出工具更換其他工具,如果遺漏一個將會出現(xiàn)嚴重BUG。

    與日志輸出相似的問題在編程中經常遇到,這種跨越好幾個模塊的功能和需求被稱為橫切關注點,典型的有日志、驗證、事務管理等。

    橫切關注點容易導致代碼混亂、代碼分散的問題。而如何將很切關注點模塊化是本節(jié)的重點。
 
    B、代理模式

    傳統(tǒng)的面向對象方法很難實現(xiàn)很切關注點的模塊化。一般的實現(xiàn)方式是使用設計模式中的代理模式。代理模式的原理是使用一個代理將對象包裝起來,這個代理對象就取代了原有對象,任何對原對象的調用都首先經過代理,代理可以完成一些額外的任務,所以代理模式能夠實現(xiàn)橫切關注點。

    可能在有些程序中有很多橫切關注點,那么只需要在代理外再加幾層代理即可。以銀行賬單為例介紹一個種用Java Reflection API動態(tài)代理實現(xiàn)的橫切關注點模塊化方法。系統(tǒng)提供了一個InvocationHandler接口: 

1//系統(tǒng)提供的代理接口 
2public interface InvocationHandler { 
3    publicObject invoke(Object proxy, Method method, Object[] args)throwThrowable; 
4}

    我們需要實現(xiàn)這個接口來創(chuàng)建一個日志代理,實現(xiàn)代碼如下:

01//日志代理實現(xiàn)   
02public class LogHandler implement InvocationHandler{ 
03  
04    privateObject target; 
05  
06    publicLogHandler(Object target){ 
07        this.target = target; 
08    
09    publicObject invoke(Object proxy, Method method, Object[] args )throwThrowable{ 
10  
11        //記錄函數(shù)的初始狀況參數(shù)等信息 
12        log4j.info(“開始:方法”+ method.getName() + “參數(shù)”+Arrays.toString(args) );
13   
14  
15        Object result = method.invoke(target, args); 
16  
17        //記錄函數(shù)的執(zhí)行狀況與返回值 
18        log4j.info(“結束:方法”+ method.getName() + “返回值”+ result ); 
19  
20    }
21 }

     這樣既可以使得日志操作不再零散分布于各個模塊,易于管理。調用者可以通過如下方式調用: 

01//主函數(shù)   
02public class Main{ 
03    publicstaticvoid main(String[ ] args){ 
04        ReportGenerator reportGeneratorImpl  =newSMSReportGenerator (); 
05  
06        //通過系統(tǒng)提供的Proxy.newProxyInstance創(chuàng)建動態(tài)代理實例 
07        ReportGenerator reportGenerator = (ReportGenerator ) Proxy.newProxyInstance(  
08            reportGeneratorImpl.getClass().getClassLoader(), 
09            reportGeneratorImpl.getClass().getInterfaces(), 
10            newLogHandler(reportGeneratorImpl)
11        ) ; 
12        ...
13    }
14}

    代理模式很好的實現(xiàn)了橫切關注點的模塊化,解決了代碼混亂代碼分散問題,但是我們可以看出用 Java Reflection API 實現(xiàn)的動態(tài)代理結構十分復雜,不易理解,Spring框架利用了代理模式的思想,提出了一種基于JAVA注解(Annotation)和XML配置的面向切面編程方法(AOP ,Aspect-Oriented Programming)簡化了編程過程。

    C、Spring AOP 使用方法

    Spring AOP使用中需要為橫切關注點(有些時候也叫切面)實現(xiàn)一個類,銀行賬單的例子中,切面的實現(xiàn)如下:

01//切面模塊實現(xiàn)   
02@Aspect    //注解1 
03public class LogAspect{ 
04  
05    @Before(“execution(* *.*(..))”)   //注解2 
06    publicvoidLogBefore(JoinPoint joinPoint) throwThrowable{ 
07        log4j.info(“開始:方法”+ joinPoint.getSignature().getName() ); 
08    
09  
10    @After(“execution(* *.*(..))”)    //注解3 
11    publicvoidLogAfter(JoinPoint joinPoint) throwThrowable{ 
12        log4j.info(“結束:方法”+ joinPoint.getSignature().getName() ); 
13    }
14}

     注解1表示這個類是一個切面,注解2中" * *.*(..)* "是一個通配符,表示在容器中所有類里有參數(shù)的方法。@Before(“execution(* *.*(..))”)表示在所有類里有參數(shù)的方法前調用切面中德 LogBefore() 方法。同理,注解3中@After(“execution(* *.*(..))”)表示在所有類里有參數(shù)的方法執(zhí)行完后調用切面中的LogAfter()方法。
    實現(xiàn)完切面類后,還需要對Spring工程中的application.xml進行配置以便實現(xiàn)完整的動態(tài)代理: 

01<?xmlversion="1.0"encoding="UTF-8"?> 
02<beans   xmlns="http://www.springframework.org/schema/beans"
03    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
04    xmlns:p="http://www.springframework.org/schema/p"
05    xmlns:aop="http://www.springframework.org/schema/aop"    
06    xsi:schemaLocation="http://www.springframework.org/schema/beans 
07        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
08        http://www.springframework.org/schema/aop 
09        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd" > 
10  
11    <aop:aspectj-autoproxy/> 
12    <beanid="smsReportGenerator"class="bank.SMSReportGenerator"/> 
13    <beanid="reportService"class="bank.ReportService"
14        <propertyname="reportGenerator"ref="smsReportGenerator"/> 
15    </bean
16    <beanclass="bank.LogAspect"/>
17</beans>

    這比之前IoC依賴關系配置的XML文件多了:xmlns:aop=http://www.springframework.org/schema/aop;http://www.springframework.org/schema/aop;http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
這3個主要是聲明XML中用于AOP的一些標簽, <bean class="bank.LogAspect" /> 是在容器中聲明LogAspect切面,<aop:aspectj-autoproxy />用于自動關聯(lián)很切關注點(LogAspect)與核心關注點(SMSReportGenerator,ReportService)。不難發(fā)現(xiàn)Spring AOP的方法實現(xiàn)橫切關注點得模塊化要比用Java Reflection API簡單很多。

6、Spring總結

 

         銀行月賬單報表例子通過使用Spring框架后變成了如下結構:

    在Spring框架的基礎上原來存在耦合的程序被分成松耦合的三個模塊。無論那個模塊修改,對其他模塊不需要額外改動。這就完成了一種良好的架構,使軟件易理解,模塊分工明確,為軟件的擴展、驗證、維護、分工提供了良好基礎。這就是Spring框架作用。當然Spring除了容器、控制反轉、面向切面之外還有許多其他功能,但都是在這三個核心基礎上實現(xiàn)的。

本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
演進式例解控制反轉(IoC)、依賴注入(DI)之一
Spring 系列: Spring 框架簡介
Struts,Hibernate,Spring經典面試題 - xp1204的專欄 - CS...
Spring 入門教程
Spring容器對Bean的管理
@PostConstruct 和 @PreDestroy 使用
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服