轉自:http://blog.csdn.net/nrain2/article/details/45459311
1,關於spring容器:
spring容器是Spring的核心,該 容器負責管理spring中的java組件,
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");//這種方式實例化容器,容器會自動預初始化全部Bean實例
ctx.getBean("beanName");
ApplicationContext 實例正是Spring容器。
ApplicationContext容器默認會實例化全部的singleton Bean
Spring容器並不強制要求被管理組件是標準的javabean。
2,Spring的核心機制:依賴注入。
無論是依賴注入(Dependency Injection)仍是控制反轉(Inversion of Conctrol),其含義徹底相同:
當某個java實例(調用者)須要調用另外一個java實例(被調用者)時,傳統狀況下,經過調用者來建立被調用者的實例,一般經過new來建立,
而在依賴注入的模式下建立被調用者的工做再也不由調用者來完成,所以稱之爲"控制反轉";建立被調用者實例的工做一般由Spring來完成,而後注入調用者,因此也稱之爲"依賴注入"。
3,依賴注入通常有2中方式:
設置注入:IoC容器使用屬性的setter方式注入被依賴的實例。<property name="" ref="">
構造注入:IoC容器使用構造器來注入被依賴的實例。<constructor-arg ref="">
配置構造注入的時候<constructor-arg>能夠配置index屬性,用於指定該構造參數值做爲第幾個構造參數值。下表從0開始。
4,Spring容器和被管理的bean:
Spring有兩個核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。他們均可以表明Spring容器。
Spring容器是生成Bean實例的工廠,並管理Spring中的bean,bean是Spring中的基本單位,在基於Spring的java EE工程,全部的組件都被當成bean處理。
包括數據源、Hibernate的SessionFactory、事務管理器。
①Spring容器:Spring最基本的接口就是BeanFactory,
BeanFactory有不少實現類,一般使用XmlBeanFactory,可是對於大部分的javaEE應用而言,推薦使用ApplictionContext,它是BeanFactory的子接口,
ApplictionContext的實現類爲FileSystemXmlApplicationContext和ClassPathXmlApplicationContext
FileSystemXmlApplicationContext:基於文件系統的XML配置文件建立ApplicationContext;
ClassPathXmlApplicationContext:基於類加載路徑下的xml配置文件建立ApplicationContext。
②ApplicationContext的事件機制,
ApplicationContext事件機制是基於觀察者設計模式實現的。經過ApplicationEvent類和ApplicationListener接口,
其中ApplicationEvent:容器事件,必須由ApplicationContext發佈;
ApplicationListener:監聽器,可有容器內的任何監聽器Bean擔任。
③容器中bean的做用域:
singleton:單例模式,在整個Spring IoC容器中,使用singleton定義的bean將只有一個實例;
prototype:原型模式,每次經過容器的getBean方法獲取prototype定義的Bean時,都將產生一個新實例;
request:對於每次HTTP請求中,使用request定義的bean都將產生一個新實例,只有在web應用程序使用Spring時,該做用域纔有效;
session:同理
global session:同理
注意:request和session做用域只在web應用中才生效,而且必須在web應用中增長額外的配置纔會生效,爲了讓request,session兩個做用域生效,必須將HTTP請求對象綁定到爲該請求提供服務的線程上,這使得具備request和session做用域的Bean實例可以在後面的調用鏈中被訪問。
當支持Servlet2.4及以上規範的web容器時,咱們能夠在web應用的web.xml增長以下Listener配置,該Listener負責爲request做用域生效:
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
若是僅使用了支持Servlet2.4之前規範的web容器,則該容器不支持Listener規範,故沒法使用這種配置,可使用Filter配置方式,咱們能夠在web應用的web.xml增長以下Filter配置:
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
再以下面的代碼:
<bean id="p" class="lee.Person" scope="request"/>
這樣容器就會爲每次HTTP請求生成一個lee.Person的實例當該請求響應結束時,該實例也隨之消失。
若是Web應用直接使用Spring MVC做爲MVC框架,即便用SpringDispatchServlet或DispatchPortlet來攔截全部用戶請求,則無需這些額外的配置,由於SpringDispatchServlet或DispatchPortlet已經處理了全部和請求有關的狀態處理。
④獲取容器的引用:
一般狀況下:
Bean無需訪問Spring容器,而是經過Spring容器訪問的,即便 須要手動訪問Spring容器,程序也已經過相似下面的代碼獲取Spring容器 的引用。
ApllicationContext cts = ClassPathApplalicationContext("bean.xml");
但在一些極端的狀況下,可能Bean須要訪問Spring容器。Spring提供另外一種方法訪問Spring容器:
實現BeanFactoryAware接口的Bean,擁有訪問Spring容器的能力,實現BeanFactoryAware的Bean被容器實例化後,會擁有一個引用指向建立他的BeanFactory。BeanFactoryAware只有一個方法setBeanFactory(BeanFactory beanFactory)該參數指向建立他的BeanFactory。
缺點:污染了代碼,使代碼與Spring接口耦合在一塊兒,所以沒有特別的必要,建議不要直接訪問容器。
5,Bean實例的建立方式及對應配置:
建立Bean的方法:
①調用構造器建立Bean實例;
②調用靜態工廠方法建立Bean;
③調用實例工廠建立Bean。
調用靜態工廠方法建立Bean:
class屬性是必須的,但此時的class並非指定Bean實例的實現類而是靜態工廠類。採用靜態工廠類須要配置以下兩個屬性:
class靜態工廠類的名字;
factory-method工廠方法(必須是靜態的)。
若是靜態工廠的方法有參數經過<constructor-arg/>元素知道。
調用實例工廠方法建立Bean:
使用實例工廠Bean時class屬性無需指定,因Spring容器不會直接實例化該Bean,
建立Bean時須要以下屬性:
factory-bean:該屬性爲工廠Bean的ID;
factory-method:該屬性是定實例工廠的工廠方法。
6,入理解Spring容器中的Bean:
抽象Bean:
全部的抽象Bean,就是是定abstract屬性爲true的Bean,抽象Bean不能被實例化,抽象Bean的價值在於被繼承
使用子Bean:
隨着應用規模的增大,Spring配置文件的增加速度更快。當應用中的組件愈來愈多,,Spring中的Bean配置也隨之大幅度增長。
就會出現一中現象:有一批配置Bean的信息徹底相同,只有少許 的配置不一樣。怎麼解決呢?
這時候就能夠用Bean的繼承來解決。
注意:子Bean沒法從父Bean繼承以下屬性:
depends-on,aotuwirwe,dependency-check,singleton,scope,lazy-iniyt這些屬性老是子Bean定義,或採用默認值。
經過 爲一個<bean.../> 元素指定parent屬性,便可指定該Bean是一個子Bean。
Bean繼承與java中繼承的區別:
Spring中的子bean和父Bean能夠是不一樣類型,但java中的繼承則可保證子類是一種特殊的父類;
Spring中的Bean的繼承是實例之間的關係,所以只要表如今參數值的延續,而java中的繼承是類之間的關係,主要表現爲方法、屬性之間的延續;
Spring中的子Bean不能夠做爲父Bean使用,不具有多態性,java中的子類徹底能夠當成父類使用。
Bean的生命週期:
①singleton與prototype的區別:
singleton:Spring能夠精確的知道該Bean什麼時候被建立、初始化、銷燬。對於singleton做用域的Bean,每次客戶端請求Spring容器總會返回一個共享的實例。
prototype:Spring容器僅僅負責建立Bean,當容器建立了Bean的實例後,Bean實例徹底交給客戶端代碼管理,容器不在跟蹤其生命週期。
每次客戶端請求prototype做用域的Bean,都會爲他建立一個新的實例,
②依賴關係注入後的行爲:
Spring提供兩種方法在Bean所有屬性設置成功後執行特定的行爲:
使用init-method屬性;
該Bean實現InitializingBean接口
第一種方法:使用init-method屬性指定某個方法在Bean所有屬性依賴關係設置結束後自動執行。使用這種方法不須要將代碼與Spring的接口耦合在一塊兒,代碼污染少;
第二種方法:實現Initializing接口,該接口有一個方法void afterPropertiesSet() throws Exception,雖然實現次接口同樣能夠在Bean所有屬性設置成功後執行特定的行爲,可是污染了代碼,是侵入式設計,所以不推薦使用。
注意:若是即採用init-method屬性指定初始化方法,又實現InitializingBean接口來指定初始化方法,先執行initializingBean接口中定義的方法,再執行init-method屬性指定的方法。
③Bean銷燬以前行爲:
與定製初始化類似,Spring也提供兩種方式定製Bean實例銷燬以前的特定行爲,以下:
使用destroy-method屬性:
實現DisposableBean接口:
注意:若是即採用destroy-method屬性指定銷燬以前的方法,又實現DisposableBean接口來指定指定銷燬以前的方法,與②相似。
④default-init-method與default-destroy-method屬性,指定了全部的Bean都會執行此方法,而不是單個的Bean。
協調做用域不一樣步的Bean:
描述:
當Spring容器中做用域不一樣的Bean相互依賴時,可能出現一些問題:
當兩個singleton做用域Bean存在依賴關係時,或當prototype做用依賴singleton做用域的Bean時,經過屬性定義依賴關係便可。、
可是,當singleton做用域的Bean依賴prototype做用域Bean時,singleton做用域的Bean只有一次初始化的機會,他的依賴關係也只有在初始化階段被設置,而他所依賴的prototype做用域的Bean則會不斷的產生新的Bean實例。
解決方案:
第一種:部分放棄依賴注入:singleton做用域的Bean每次須要prototype做用域的Bean,則主動向容器請求新的Bean實例。
第二種:利用方法注入。
第一種方案確定是很差的,代碼主動請求新的Bean實例,必然會致使與Spring API耦合,形成代碼嚴重污染。
一般狀況下采用第二中方式。
方法注入一般使用lookup方法注入,利用lookup方法注入可讓Spring容器重寫容器中Bean的抽象方法或具體方法,返回查找容器中的其餘 Bean,被查找的Bean一般是non-singleton Bean(儘管也能夠是singleton).
如:public class SteelAxe implements Axe{
//每執行一次加一
private int count;
public String chop(){
return ++count;
}
}
public abstract class Chinese implements Perosom{
private Axe axe;
//定義一個抽象方法,該方法將由Spring負責實現
public abstract Axe createAxe();
public voidsetAxe(Axe axe){
this axe = axe;
}
public Axe getAxe(){
return axe;
}
}
在Spring配置文件中配置:
<bean id="steelAxe" class="...SteelAxe" scope="prototype"></bean>
<bean id="chinese" class="..Chinese" >
< lookup-mehtod name="createAxe" bean="steelAxe">
<property name="axe" ref="steelAxe"/>
</bean>
容器中的工廠Bean:
此處的工廠Bean與前面介紹的實例工廠方法建立Bean、靜態工廠建立Bean有所區別:
前面的那些工廠是標準的工廠模式,Spring只是負責調用工廠方法來建立Bean實例;
此處工廠Bean是Spring的一種特殊Bean,這種工廠Bean必須實現FactoryBean接口。
FactoryBean接口是工廠Bean標準的工廠Bean的接口,實現該接口的Bean只能當工廠Bean使用,當咱們將工廠Bean部署在容器中,並經過getBean()方法來獲取工廠Bean,容器不會返回FactoryBean實例而是FactoryBean的產品。
FactoryBean提供了三個方法:
Object getObject();
Class getObjectType();
boolean isSingleton();
如:
public class PersonFactory implements FactoryBean{
Person p = null;
public Object getObject() throws Exception{
if(p==null){
p = new Chinense();
return p;
}
}
public Class getObjectType(){
return Chinese.class;
}
public boolean isSingleton(){
return true;
}
}
<!--配置一個FactoryBean,和普通的Bean同樣-->
<bean id="chinese" class=""/>
public static void main(String args[]){\
//以classpth下的bean.xml建立Reource對象
ClassPathResource re = new ClasspathResource("bean.xml");
//建立BeanFactory
XmlBeanFactory factory = new XmlBeanFactory(re);
Person p = (Person)factory.getBean("chinese");
//如須要獲取FactoryBean自己則應該在bean id前加&
Person p = (Person)factory.getBean("&chinese");
}
對於初學者可能沒法體會到工廠bean的做用,實際上,FactoryBean是Spring中很是有用的接口。例如:TransationProxyFactroyBean,這個工廠轉爲目標Bean建立事務代理.
7,深刻理解依賴關係配置
組件與組件之間的耦合,採用依賴注入管理,可是普通的javabean屬性值,應直接在代碼裏設置。
對於singleton做用域的bean,若是沒有強制取消其預初始化行爲,系統會在建立Spring容器時預初始化全部的singleton做用域的bean,與此同時,該bean依賴的bean也一塊兒被實例化。
BeanFactory與ApplicationContext實例化容器中的bean的時機不一樣,前者等到程序須要Bean實例才建立Bean
後者會預初始化容器中的全部Bean。
由於採用ApplicationContext做爲Spring的容器,建立容器時,會建立容器中全部singleton做用域的全部bean,所以可能須要更多的系統資源,可是一旦建立成功。應用後面的 響應速度會很快,所以,對於普通的javaEE而言 ,建議使用ApplicationContext做爲Spring的容器。
Bean實例4中屬性值的設置:
value;ref;bean;list、set、map、props
①設置普通屬性值value,略;
②配置合做者Bean ref
能夠爲ref元素指定兩個屬性:bena、Local
bean:引用在不一樣一份XML配置文件中的其餘Bean實例的ID屬性值;
Local:引用同一份XML配置文件的其餘Beanid屬性值。
也能夠不配置以上兩個屬性。
③組合屬性名稱:
public class A{
private Person p = new Person();
set/get....
}
Spring配置文件
<bean id="a" class="A">
<property name="p.name" value="aaa"/>
</bean>
④注入嵌套Bean:
<bean id="" class="">
< property name="">
//屬性爲嵌套Bean 不能由Spring容器直接訪問,所以沒有id屬性
<bean class="..."/>
</property>
</bean>
⑤注入集合值:
<list>
<value></value>
<value></value>
</list>
<map>
//每個entry配置一個key-value對
<entry>
<key>
<value>.</value>
</key>
<value></value>
</entry>
</map>
<set>
<value></value>
<bean></bean>
<ref local=""/>
</set>
<props>
<prop key="">.....</prop>
<prop key="">.....</prop>
</props>
⑥注入方法返回值:
public class ValueGenrator{
public int getValue(){
return 6;
}
public static int getStaticValue(){
return 9;
}
}
<bean id="valueGenrator" class="lee.ValueGenrator"/>
<bean id="son1" class="Son">
<property name="age">
<bean class="org.springframework.bean.factory.congfig.MethodInvokignFactoryBean">
//配置非靜態方法
<property name="targetObject" ref="valueGenrator"/>
//配置靜態方法
<!--
<property name="targetClass" value="lee.ValueGenrator"/>
-->
<property name="targetMehtod" value="getStaticValue/>
</property>
</bean>
8,強制初始化Bean:
Spring有一個默認的規則,老是先初始化主調Bean,而後在初始化依賴Bean。
爲了指定Bean在目標Bean以前初始化,可使用depends-on屬性
9,自動裝配:
Spring能自動裝配Bean與Bean之間的依賴關係,即便無需使用ref顯式指定依賴Bean。
Spring的自動裝配使用autowire屬性指定,每個<bean/>元素均可以指定autowire屬性,也就是說在Spring容器中徹底可讓某些Bean自動裝配,而某些Bean不沒使用自動裝配。
自動裝配能夠減小配置文件的工做量,可是下降了依賴關係的透明性和依賴性。
使用autowire屬性自動裝配,autowire屬性能夠接受以下幾個值 :
no:不使用自動裝配。這是默認配置。
byName:根據屬性自動裝配,BeanFactory會查找容器中全部的Bean,找出id屬性與屬性名同名的Bean來完成注入。若是沒有找到匹配的Bean,Spring則不會進行任何注入。
byType:根據屬性類型自動裝配,BeanFactroy會查找容器中全部的 Bean,若是一個正好與依賴屬性類型相同的Bean,就會自動注入這個屬性。
若是有多個這樣的Bean則會拋出異常。若是沒有這樣 的Bean則什麼也不會發生,屬性不會被設置。
constructor:與byType相似,區別是用於構造注入的參數,若是BeanFactory中不是剛好有一個Bean與構造器參數類型相同。則會拋出異常。
autodetect:BeanFactory會根據Bean內部的結構,決定使用constructor或byType,若是找到一個缺省的構造器,就會應用byType。
注意:對於大型的應用而言,不鼓勵使用自動裝配,
10,依賴檢查:
Spring提供一種依賴檢查的功能,能夠防止出現配置手誤,或者其餘狀況的錯誤。
使用依賴檢查可讓系統判斷配置文件的依賴關係注入是否徹底有效。
使用依賴檢查,能夠保證Bean的屬性獲得了正確的設置,有時候,某個Bean的特定屬性並不須要設置值,或者某些屬性已有默認值,此時採用依賴檢查就會出現錯誤,該Bean就不該該採用依賴檢查,幸虧Spring能夠爲不一樣的Bean單獨指定依賴檢查的行爲,Spring提供dependency-chech屬性來配置依賴檢查,固然也能夠指定不一樣的檢查依賴策略。
該屬性有以下值:
none:不進行依賴檢查,沒有指定值的Bean屬性僅僅是沒有設置值,這是默認值。
simple:對基本類型和集合(除了合做者Bean)進行依賴檢查。
objects:僅對合做者Bean進行依賴檢查。
all:對合做者Bean、基本數據類型所有進行依賴檢查。
public class Chinese implements Person{
private Axe axe;
private int age = 30;
//implements method
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age
}
}
<bean id="axe" class="StoneAxe"/>
<bean id="chinese" class="Chinese" dependency-check="all">
<property name="axe" ref="axe"/>
</bean>
以上程序將會拋出異常,雖然Chinese類的age屬性已經有了默認值,但配置dependency-check="all"則要求配置文件爲全部的屬性都提供正確的值。
而age屬性沒有提供值。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二:深刻Spring:
1,利用後處理器擴展Spring容器:
Spring容器提供了很好的擴展性,除了能夠與各類第三方框架良好的整合外,其IoC容器也容許開發者進行擴展,這種擴展甚至無需實現BeanFactory或ApplicationContext接口,容許兩個後處理器對IoC容器進行擴展:
Bean後處理器:這種處理器會對容器中的Bean進行後處理,對Bean的功能進行額外的增強。
容器後處理器:這種處理器對IoC容器進行後處理,用於加強容器的功能。
①Bean後處理器:
Bean後處理器是一種特殊的Bean,這種特殊的Bean並不對外提供服務,它甚至能夠沒有id\屬性。它主要負責對容器中的其餘Bean執行後處理。
後處理器必須實現BeanPostProcessor接口。此接口有兩個方法:Object postProcessorBeforeInitialization(Object object,String name) Object postProcessorAfterInitialization(Object object,String name)
第一個參數是系統即將後處理的Bean的實例,第二個參數是該Bean實例的名稱。
public class MyBeanPostProcessor implements BeanPostProcessor{
public Object postProcessorBeforeInitialization(Object object,String name) throws Beanexception{
System.out.println("後處理器在初始化以前對"+"name"+"進行加強處理");
return object;
}
public Object postProcessorAfterInitialization(Object object,String name) throws Beanexception{
System.out.println("後處理器在初始化以後對"+"name"+"進行加強處理");
if(object intanceof Chinese ){
Chinese c = (Chinese)bean;
bean.setName="張三";
}
return object;
}
}
public class Chinese implements Person{
private Axe axe;
private String name;
public void setName(String name){
System.out.println("Spring執行注入.......");
this.name=name;
}
//set/get....
public void init(){
System.out.println("初始化工做init");
}
...............................
}
<bean id="steelAxe" class="..."/>
<bean id="chinese" class="Chinese">
<property name="axe" ref="steelAxe"/>
<prperty name="name" value="李四"/>
</bean>
<!--配置後處理器-->
<bean id="beanPostProcessor" class="MyBeanPostProcessor"/>
public static void mian(String args[]){
ClassPathResource rs = new ClasspathReource(bean.xml);
XmlBeanFactory factory = new XmlBeanfactory(rs);
MyBeanPostProcessor processor = (MyBeanPostProcessor)factory.getBean("beanPostProcessor")
//註冊BeanPostProcessor
factory.addPostProcessor(processor )
Person p = (Person)factory.getBean("chinese");
p.useAxe();
}
程序輸出name屬性時,不會輸出李四,而是張三,雖然注入的值是李四,可是Bean後處理器發生做用。
輸出:
---------------------------------------------------------------
Spring執行注入依賴關係......
後處理器在初始化以前對chinese進行加強處理
初始化工做init
後處理器在初始化以後對chinese進行加強處理
張三
----------------------------------------------------------------
若是使用BeanFactory做爲Spring的容器。則必須手動註冊Bean後處理器,容器中一旦註冊了Bean後處理器,Bean後處理器就會自動啓動,在容器中每一個Bean建立時自動工做。
實現BeanPostProcessor接口的Bean後處理器能夠對Bean進行任何操做,包括徹底忽略這個回調,BeanPostProcessor一般用來檢查標記接口,或者作將Bean包裝成一個Proxy的事情。
若是不採用BeanFactory做爲Spring的容器,採用ApplicationContext做爲容器,則無需手動註冊Bean後處理器,ApplicationContext可自動檢測到容器中的Bean後處理器,自動註冊。Bean後處理器會在建立Bean時自動啓動。對於後處理器而言,ApplicationContext做爲Spring容器更爲方便。
②容器後處理器:
除了上面的提供的Bean後處理器外,Spring還提供了一種容器後處理器,,Bean後處理器負責容器中全部Bean實例的,而容器後處理器負責處理容器自己。
容器後處理器必需要實現BeanFactoryPostProcessor接口。
相似於Bean後處理器,採用ApplicationContext做爲容器,則無需手動註冊容器後處理器.若是使用BeanFactory做爲Spring的容器。則必須手動註冊容器後處理器.
2,Spring的「零配置」支持
①搜索Bean類
Spring提供以下幾個Annotation來標註Spring Bean:
@Component標註一個普通的Spring Bean;
@Controller:標註一個控制器組件類;
@Service:標註一個業務邏輯組件類;
@Repository:標註一個Dao組件;
<context:component-scan base-package=""/>
在默認的狀況下,自動搜索全部以@Component、@Controller、@Service、@Repository註釋的java類,看成Spring Bean處理。
還能夠經過<context:component-scan base-package=""/>子元素<include-filter>/<exclude-filter>
②指定Bean的做用域:
如:
@Scope("prototype")
@Component("axe")
public class SteelAxe implements Axe{
}
③使用@Resource配置依賴:
@Resource有一個name屬性,在默認的狀況下,Spring將這個值 解釋爲須要被注入的Bean的實例的名字,換句話說,使用@Resource與<property../>元素的ref屬性具備相同的效果。
當使用@Resource修飾setter方法,若是省略name屬性,則name屬性默認是從該setter方法去掉set子串,首字母小寫的到的子串。
當使用@Resource修飾Field時,若是mame,則默認與Field的相同。
④自動裝配和精確裝配:
Spring提供@Autowired來指定自動裝配,使用@Autowired能夠標誌setter方法、普通方法、和構造器。如:
@Component
public class Chinese implements Person{
@Autowired
private Aex axe;
@Autowired
public Chinese(Axe axe){
this.axe = axe
}
@Autowired
private Axe [] axes;
}
當@Autowired標註Field時Spring會把容器中的與該Field類型匹配的Bean注入該屬性,
若是Spring容器中有多個同類型的Bean與Field類型匹配,則會出現異常。
當@Autowired標註數組類時,在這種狀況,Spring會自動搜索Spring容器中全部與數組類型相匹配的類型的Bean,並把這些Bean看成數組的元素來建立數組。
當@Autowired標註集合時,和標註數組相似,當時必須使用泛型.
⑤使用@Qualifier精確裝配
正如上面的@Autowired老是採用byType的自動裝配策略。在這種狀況下,符合自動和裝配的類型的Bean經常有多個這個時候就會引起異常了。
爲了實現精確的配置,Spring提供@Qualifier,能夠根據Bean標識來指定自動裝配,
--- @Qualifier能夠標註Field,如:
@Component
public class Chinese implements Person{
@Autowired
@Qualifier("steelAxe")
private Aex axe;
}
--- @Qualifier還能夠標註方法的形參,如:
@Component
public class Chinese implements Person{
@Autowired
private Aex axe;
public void setAxe(@Qualifier("steelAxe") Axe axe )
this.axe=axe;
}
}
3,資源訪問:
4,Spring的AOP:
①AspectJ
②AOP的基本概念:
AOP框架不與特定的代碼耦合.下面是一些面向切面編程的術語:
切面(Aspect):業務流程運行到某個特定的步驟,也就是一個應用運行的關注點,關注點可能橫切多個對象,因此經常也被稱爲橫切關注點。
鏈接點(Joinpoint):程序執行過程明確的點,如方法的調用、異常的拋出,Spring AOP中鏈接點老是方法的調用。
加強處理(Advice):AOP框架在特定的切入點執行的加強處理,處理有around、before和After等類型。
切入點(Pointcut):能夠插入加強處理的鏈接點,簡而言之,當某個鏈接點知足指定要求時,該鏈接點將被添加加強處理,該鏈接點也就變成了切入點,
例如一下代碼:
pointcut xxxPointcut(){
:execution(void H*.say*());
}
每一個方法的調用都只是鏈接點,但若是該方法屬於H開頭的類,且方法名以say開頭,按照方法的執行將變成切入點。
引入:將方法添加到被處理的類中,Spring容許引入新的接口到任何被處理的對象。例如:你可使用一個引用,使任何對象實現IsModified接口,以此簡化緩存。
目標對象:被AOP加強處理的對象,也被稱爲被加強的對象,若是AOP框架是經過運行時代理來實現的,那麼這個對象將是一個被代理的對象。
AOP代理:AOP框架建立的 對象,簡單的說,代理就是對目標對象的增強,Spring的AOP代理可使JDK動態代理,也能夠是CGLIB代理,前者爲實現接口的目標對象的代理後者爲不實現接口的目標對象的代理。
織入(Weaving):將加強處理增長到目標對象中,並建立一個被加強的對象(AOP代理)的過程就是織入。織入有兩種實現方式:編譯時加強(例如AspectJ)和運行時加強(例如CGLIB)
目前Spring只支持將方法調用做爲鏈接點,若是須要把Field的訪問和更新也做爲加強處理的鏈接點,可使用AspectJ。
一旦咱們掌握了上面的AOP的相關概念,不難發現進行AOP的編程實際上是一件很簡單的事情,縱觀AOP編程須要程序員參與的只有3個部分:
定義普通業務組件;
定義切入點,一個切入點橫切多個業務組件;
定義加強處理,加強處理就是如今AOP框架爲普通業務組件織入的處理動做。
③AOP,基於Annotation的零配置方式:
定義Before加強處理:
當咱們在一個切面類裏使用@Before來標註一個方法時,該方法將做爲Before加強處理,使用@Before標註時一般要指定一個value屬性值,用來指定一個切入點表達式(既能夠是一個已有的切入點,也能夠直接定義切入點表達式),用於指定該加強處理將被織入那些切入點。
注意:("execution(* cn.huaxia.spring.*.*(..))")第一個星號後面必定要有一個空格。
//定義一個切面
@Aspect
publicclass BeforeAdviceTest {
// 執行cn.huaxia.spring包下的全部方法都作爲切入點
@Before("execution(* cn.huaxia.spring.*.*(..))")
publicvoid authority() {
System.out.println("模擬執行權限檢查...");
}
}
上面@Aspect@標註BeforeAdviceTest 代表該類是一個切面類,在該切面裏定義了一個authority()方法,這個方法原本沒有什麼特別之處,可是由於使用@Before來標註該方法,這就將該方法轉換成一個加強處理。上面程序中使用@Before Annotation標註時,直接指定切入點表達式,指定cn.huaxia.spring下的全部方法都做爲切入點。
@Component
publicclass Chineseimplements Person {
@Override
public String sayHello(String word) {
return word;
}
publicvoid eat(String food) {
System.out.println("我正在吃" + food);
}
}
從上面Chinese類的代碼來看,他是一個如此純淨的Java類,他絲絕不知將被誰來進行加強處理,也不知道將被怎樣加強處理---正式這種無知纔是"AOP"最大的魅力:目標類能夠被無限加強。
在Spring配置文件中配置自動搜索Bean組件,配置自動搜索切面類,Spring AOP自動對Bean組件進行加強。配置文件以下:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 指定自定搜索Bean組件、自動搜索切面類-->
<context:component-scanbase-package="cn.huaxia.spring">
<context:include-filtertype="annotation"
expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
<!-- 啓動AspectJ支持-->
<aop:aspectj-autoproxy/>
</beans>
編寫Junit類:
publicclass TestBefore {
static ApplicationContextac =null;
@BeforeClass
publicstaticvoid setUpBeforeClass() throws Exception {
ac =new ClassPathXmlApplicationContext("beans.xml");
}
@Test
publicvoid testBefore() {
//Chinese c = (Chinese)ac.getBean("chinese");//會出錯,不能轉換爲Chinese
Person c = (Person)ac.getBean("chinese");
c.eat("西瓜");
System.out.println(c.sayHello("你好--Before"));
}
}
定義AfterReturning加強處理:
AfterReturning加強處理在目標方法正常完成以後織入。
相似於使用@Before Annotation可標註Before加強處理,使用@AfterReturning Annation來標註@AfterRetuning加強處理。
使用@AfterReturning Annotation能夠指定以下屬性:
pointcut/value:這兩個屬性的做用是同樣的,他們都是指定該切入點對應的切入表達式,當指定pointcut屬性,value屬性值將會被覆蓋。
returning:指定一個返回值形參,加強處理的方法能夠經過該形參名來訪問目標方法的返回值。
@Aspect
publicclass AfterReturningAdviceTest {
@AfterReturning(pointcut="execution(* cn.huaxia.spring.*.*(..))",returning="obj")
publicvoid log(Object obj){
System.out.println("獲取目標方法返回值"+obj);
System.out.println("模擬日誌記錄功能....");
}
}
若是log有參數,returning屬性必定要配置。若是參數不止一個,那麼目標方法將不會執行必需要配置args表達式。
注意:使用returning屬性還有另外一個額外的做用:它能夠限定切入點只匹配具備對應返回類型的方法。加入上面的log的參數是String類型,則該切入點只匹配cn.huaxia.spring包下的返回值類型爲String的全部方法。雖然AfterReturning加強處理能夠訪問到目標方法的返回值,但不能夠改變目標方法返回值。
定義AfterThrowing加強處理:
AfterThrowing加強處理主要用於程序中未處理的異常。
使用@AfterThrowing Annotation時能夠指定以下兩個屬性:
pointcut/value
throwing:指定一個返回值形參名,加強處理定義的方法能夠經過該形參名來訪問目標方法中所拋出的異常對象。
@Aspect
publicclass AfterThrowingAdviceTest {
@AfterThrowing(pointcut ="execution(* cn.huaxia.spring.*.*(..))", throwing ="th")
publicvoid doRecoveryActions(Throwable th) {
System.out.println("目標方法中拋出的異常" + th);
System.out.println("模擬拋出異常的加強處理/.....");
}
}
@Component
publicclass Chineseimplements Person {
@Override
publicString sayHello(String word) {
try {
System.out.println("sayHello方法開始執行...");
new FileInputStream("a.txt");
}catch (FileNotFoundException e) {
e.printStackTrace();
}
word;
}
publicvoid eat(String food) {
System.out.println("我正在吃" + food);
}
publicvoid divide(){
int a = 5/0;
System.out.println("divide執行完畢/"+a);
}
}
//Chinese c = (Chinese)ac.getBean("chinese");//會出錯,不能轉換爲Chinese
Person c = (Person)ac.getBean("chinese");
System.out.println(c.sayHello("你好--Before"));
c.eat("西瓜");
c.divide();
}
輸出的結果:
--------------------------------------------------- -----------------------------------------------------
--------------------------------------------------- -----------------------------------------------------
上面的程序的sayHello和divided兩個方法都會拋出異常,可是sayHello方法中的異常將由該方法顯示捕捉,因此Spring AOP不會處理該異常。
而divide方法將拋出算術異常,且該異常沒有被任何程序所處理,故Spring AOP會對該異常進行處理。
使用throwing屬性還有另外一個做用:它能夠用於限定切入點只匹配指定類型的異常。相似AfterRetuning的returning 屬性。
Spring AOP的AfterThrowing處理雖然能夠對目標方法的異常進行處理,可是這種處理與直接使用catch捕捉是不一樣的:
catch意味徹底處理該異常,若是catch沒有從新拋出一次異常,則方法能夠正常結束,而AfterThrowing處理雖然處理了異常,但它不能徹底處理異常,該異常依然會傳播到上一級調用者。
定義After加強處理:
After加強處理與AfterReturning加強處理有點類似,可是也有不一樣:
AfterRetuning只在目標方法成功完成後才被織入。
After加強處理無論目標方法如何結束,都會被織入。
由於一個方法無論是如何結束,After加強處理它都會被織入,所以After加強處理必須處理正常返回和異常返回兩種狀況,這種加強處理一般用於資源釋放。
使用@After Annotation標註一個方法,便可將該方法轉成After加強處理,使用@After Annotation加強處理須要指定一個value屬性,該屬性值用於指定該加強處理被織入的切入點,既能夠是一個已有的切入點也能夠直接指定切入點表達式。
@Aspect
publicclass AfterAdviceTest {
@After("execution(* cn.huaxia.spring.*.*(..))")
publicvoid release(){
System.out.println("模擬方法結束後資源釋放....");
}
}
結果:
--------------------------------------------------- -----------------------------------------------------
--------------------------------------------------- -----------------------------------------------------
從上面的輸出結果能夠看出,After加強處理很是相似於異常處理中的finally塊的做用-----不管結果如何,它總在方法執行結束以後被織入,所以特別適合進行資源回收。
Around加強處理:
@Around Annotation用於標註Around的加強處理,Around的加強處理是比較強的加強處理,它近似等於Before和AfterRetuning加強處理的總和,Around加強處理既能夠在目標方法以前執行,也能夠在執行目標方法以後織入加強動做。
與Before加強處理和AfterReturning加強處理不一樣的是Around加強處理能夠決定目標方法執行在何時執行,如何執行,甚至能夠阻止目標方法執行。Around加強處理能夠改變執行目標方法的參數值,還能夠改變執行目標方法以後的返回值。
使用@Around Annotation時須要指定value屬性,該屬性用來指定該加強處理被織入的切入點。
當定義一個Around加強處理方法時,該方法的第一個形參必須是ProceedingJoinPoint類型(至少包含一個形參)。
在加強處理方法體內,調用ProceedingJoinPoint的process方法時,還能夠傳入一個Object[]對象,該數組中的值將被傳入目標方法做爲執行方法的的實參。
-------這就是Around加強處理能夠徹底控制目標方法執行時機、如何執行的關鍵。若是程序沒有調用ProceedingJoinPoint的proceed方法則目標方法不會執行。
@Aspect
publicclass AroundAdviceTest {
@Around("execution(* cn.huaxia.spring.*.*(..))")
public Object proceedTX(ProceedingJoinPoint pre)throws Throwable {
System.out.println("開始事務.....");
Object obj = pre.proceed(new String[] {"被修改的參數" });
System.out.println("結束事務.....");
return obj +",新增的內容";
}
}
結果:
--------------------------------------------------- -----------------------------------------------------
--------------------------------------------------- -----------------------------------------------------
當調用ProceedingJoinPonint的proceed方法時,傳入的Object[]參數值將做爲目標方法的參數,若是傳入的Object[]參數的長度和目標方法參數個數不匹配,或者Object[]數組元素與目標方法所需的參數不匹配,程序就會拋出異常。
爲了能獲取目標方法參數的個數和類型,這就須要加強處理方法可以訪問執行目標方法的參數了。
訪問目標方法的參數:
訪問目標方法最簡單的的作法是定義加強處理方法時將第一個參數定義爲JoinPoint類型,當加強處理方法被調用時,該JoinPoint參數就表明了織入加強處理的鏈接點,JoinPoint包含以下幾個經常使用的方法:
Object[] getArgs():返回執行目標方法的參數;
Signature getSignature():返回被加強的方法的相關信息;
Object getTarget():返回被織入加強處理的目標方法;
Object getThis():返回AOP框架爲目標對象生成的代理對象。如:
package cn.huaxia.spring;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class FourAdviceTest {
@Around("execution(* cn.huaxia.spring.service.*.*(..))")
public Object proceedTX(ProceedingJoinPoint pre) throws Throwable {
System.out.println("Around加強處理:執行目標方法前,執行模擬開啓事務.......");
Object[] objs = pre.getArgs();
if (objs != null && objs.length > 0
&& objs[0].getClass() == String.class) {
objs[0] = "被修改的參數";
}
Object obj = pre.proceed(objs);
System.out.println("Around加強處理:執行目標方法以後,模擬結束事務.....");
return obj + ",新增長的內容";
}
@Before("execution(* cn.huaxia.spring.service.*.*(..))")
public void authority(JoinPoint jp) {
System.out.println("Before加強:模擬權限檢查");
System.out.println("Before加強:被織入加強處理的目標方法:"
+ jp.getSignature().getName());
System.out
.println("Before加強:目標方法的參數爲:" + Arrays.toString(jp.getArgs()));
System.out.println("Before加強:被注入加強的處理 的目標對象:" + jp.getTarget());
}
@AfterReturning(pointcut="execution(* cn.huaxia.spring.service.*.*(..))",returning="obj")
public void log(JoinPoint jp, Object obj) {
System.out.println("AfterReturning加強:獲取目標方法的返回值:" + obj);
System.out.println("AfterReturning加強:模擬日誌記錄功能.....");
System.out.println("AfterReturning加強:被織入加強處理的目標方法:"
+ jp.getSignature().getName());
System.out.println("AfterReturning加強:目標方法的參數爲:"
+ Arrays.toString(jp.getArgs()));
System.out.println("AfterReturning加強:被注入加強的處理 的目標對象:" + jp.getTarget());
}
@After("execution(* cn.huaxia.spring.service.*.*(..))")
public void release(JoinPoint jp) {
System.out.println("After加強:模擬方法結束後,資源釋放.....");
System.out.println("After加強:被織入加強處理的目標方法:"
+ jp.getSignature().getName());
System.out.println("After加強:目標方法的參數爲:" + Arrays.toString(jp.getArgs()));
System.out.println("After加強:被注入加強的處理 的目標對象:" + jp.getTarget());
}
}
輸出結果:
Around加強處理:執行目標方法以後,模擬結束事務.....
AfterReturning加強:獲取目標方法的返回值:被修改的參數,新增長的內容
AfterReturning加強:模擬日誌記錄功能.....
AfterReturning加強:被織入加強處理的目標方法:sayHello
AfterReturning加強:目標方法的參數爲:[被修改的參數]
AfterReturning加強:被注入加強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
After加強:模擬方法結束後,資源釋放.....
After加強:被織入加強處理的目標方法:sayHello
After加強:目標方法的參數爲:[被修改的參數]
After加強:被注入加強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
被修改的參數,新增長的內容
Around加強處理:執行目標方法前,執行模擬開啓事務.......
Before加強:模擬權限檢查
Before加強:被織入加強處理的目標方法:eat
Before加強:目標方法的參數爲:[被修改的參數]
Before加強:被注入加強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
我正在吃:被修改的參數
Around加強處理:執行目標方法以後,模擬結束事務.....
AfterReturning加強:獲取目標方法的返回值:null,新增長的內容
AfterReturning加強:模擬日誌記錄功能.....
AfterReturning加強:被織入加強處理的目標方法:eat
AfterReturning加強:目標方法的參數爲:[被修改的參數]
AfterReturning加強:被注入加強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
After加強:模擬方法結束後,資源釋放.....
After加強:被織入加強處理的目標方法:eat
After加強:目標方法的參數爲:[被修改的參數]
After加強:被注入加強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
Around加強處理:執行目標方法前,執行模擬開啓事務.......
Before加強:模擬權限檢查
Before加強:被織入加強處理的目標方法:divide
Before加強:目標方法的參數爲:[]
Before加強:被注入加強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
After加強:模擬方法結束後,資源釋放.....
After加強:被織入加強處理的目標方法:divide
After加強:目標方法的參數爲:[]
After加強:被注入加強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
Spring AOP採用和AsoectJ同樣的優先順序來織入加強:在"進入"鏈接點時,最高等級的加強處理將先織入(因此給定兩個Before加強處理中,優先級高的那個會執行),在"退出"鏈接點時,最高優先等級的加強處理將會最後織入。
4個加強處理的優先等級以下(從低到高):
Before加強處理------------->Around加強處理------------->AfterReturning加強處理------------->After加強處理
當兩個不一樣的兩個加強處理須要在同一個鏈接點被織入時,Spring AOP將以隨機的順序來織入這兩個加強處理,若是應用須要指定不一樣切面里加強處理的優先級,Spring提供了兩個解決方案:
讓切面類實現org.springframework.core.Ordered接口,實現該接口提供的 int getOrder()方法,方法返回值越小,則優先級越高。
直接使用@Order Annotation 來標註一個切面類,使用@Order Annotation時能夠指定一個int 型的value屬性,該屬性值越小,優先等級越高。
當相同的切面裏的兩個加強處理須要在相同的鏈接點被織入時,Spring AOP將以隨機的方式來織入這兩個加強處理,沒有辦法指定他們的順序,若是必定要它們以特定的順序被織入,則能夠考慮把它們壓縮到一個加強處理中,或者是把它們分別放在不一樣的切面,在經過切面的優先等級來排序
若是隻要訪問目標方法的參數,Spring還提供了一個更加簡單的方式:咱們能夠在程序中使用args來綁定目標方法的參數,若是args表達是中指定一個或多個參數,則該切入點只匹配具備對應形參的方法,且目標方法的參數值將被出入加強處理方法。
如:
@Aspect
publicclass AccessArgsAspect {
@AfterReturning(pointcut ="execution(* cn.huaxia.spring.service1.*.*(..))"
+"&&args(food,time)", returning ="retValue")
publicvoid access(String food, Date time, Object retValue) {
System.out.println("目標方法中的String參數" + food);
System.out.println("目標方法的time參數" + time);
System.out.println("模擬日誌記錄功能......");
System.out.println("目標參數的返回值:"+retValue);
}
}
@Component
publicclass Chinese3implements Person2 {
@Override
public String sayHello(String word) {
return word;
}
publicvoid eat(String food, Date time) {
System.out.println("我正在吃:" + food+",如今的時間是:" + time);
}
}
結果:
爲何目標方法的返回值是null,由於該切入點只匹配
publicvoid eat(String food, Date time)方法。
從上面能夠得出,使用args表達式有以下兩個做用:
--提供更簡單的方式訪問目標方法的參數;
--可用於對切入表達式增長額外限制。
除此以外,使用args表達式時還可使用以下形式:args(name,age,..),這代表加強處理方法中可經過name,age來訪問目標方法的參數,上面表達式括號中的2點,它表示可匹配更多的參數,如:
public void doSomething(String name,int age)
這意味着只要目標方法第一個參數是String類型,第二個參數是int則該方法就能夠匹配該切入點。
定義切入點:
正如前面的FourAdviceTest程序中看到的,這個切面類定義了4個加強處理,這4個加強處理分別指定了相同的切入點表達式,這種作法顯然不符合軟件設計的原則:咱們將那個切入點表達式重複了4次,若是須要該這個切入點,那麼就要修改4處。
Spring AOP只支持以Spring Bean的方法執行組做爲鏈接點,
例如:
@Pointcut("execution(* transfer(..))")
private void anyOldTransfer(){}一旦採用上面的代碼片斷定義了名爲anyOldTrandser的切入點以後,程序就能夠重複使用該切入點了,甚至能夠在其餘切面類、其餘包的切面類裏使用該切入點,不過這取決於該方法簽名前的訪問修飾符。
切入點指示符:
正如前面的execution就是一個切入點指示符,Spring AOP僅僅支持部分AspectJ切入點指示符,不只如此Spring AOP只支持使用方法調用做爲鏈接點,因此Spring AOP的切入點指示符僅匹配方法執行的鏈接點。
Spring AOP一共支持以下幾種切入點指示符:
----execution:用於匹配執行方法的鏈接點,是Spring AOP最主要的切入點指示符,execution表達式的格式以下:
execution(modifies-pattern? ret-type-pattern declaring-type-parttern? name--pattern(parm-pattern) throws-pattern?)
以上打了問號的均可以省略。
上面格式中的execution是不變的,用於做爲execution表達式的開頭,整個表示式各個部分的解釋爲:
modifies-pattern:指定方法的修飾符,支持通配符,該部分能夠省略。
ret-type-pattern:指定方法的返回值類型,支持通配符,可使用「*」通配符來匹配全部返回值類型。
declaring-type-parttern:指定方法所屬的類,支持通配符,該部分能夠省略。
name--pattern:指定匹配指定方法名,支持通配符,可使用「*」通配符來匹配全部方法。
parm-pattern:指定方法聲明中的形參列表,支持兩個通配符:「*」、「..」,其中*表示一個任意類型的參數,而「..」表示零個或多個任意類型的參數。
throws-pattern:指定方法聲明拋出的異常,支持通配符,該部分能夠省略。
例以下面幾個execution表達式:
//匹配任意public方法的執行。
execution(public * * (..))
//匹配任意方法名以set開始的方法只想可以。
execution(* set* (..))
//匹配AccountService裏定義的任意方法的執行。
execution(* org.huaxia.AccountService.* (..))
//匹配Service包中任意類的任意方法的執行。
execution(* org.huaxia.service.*.*(..))
----within:限定匹配特定類型的鏈接點,當使用Spring AOP的時候,只能匹配方法執行的鏈接點,
例以下面幾個within表達式:
//在Service包中的任意鏈接點。
within(* org,huaxia.service.*)
//在Service包或者子包的任意鏈接點
within(* org.huaxia.service..*)
----this:用於限定AOP代理必須指定類型的實例,用於匹配該對象的全部鏈接點。當使用Spring AOP的時候,只能匹配方法執行的鏈接點。
例如:
//匹配實現了AccountService接口的代理對象的全部鏈接點
this(org.huaxia.service.AccountService)
----target:用於限定目標對象必須是指定類型的實例,用於匹配該對象的全部鏈接點,當使用Spring AOP只匹配執行方法的鏈接點。例如「」
//匹配實現了AccountService接口的目標對象的全部鏈接點
target(org.huaxia.service.AccountService)
----args:用於對鏈接點的參數類型進行限制,要求參數類型是指定類型的實例,例如:
//匹配只接受一個參數,且傳入的參數類型是Serializable的全部鏈接點
args(java.io.Serializable)
注意:該例中給出的切入點表達式與execution(* *(java.io.Serializable))不一樣:args版本只匹配動態運行時傳入參數值是Serializable類型的狀況,而execution版本只匹配方法簽名只包含一個Serializable類型的參數的方法。
----bean:用於指定只匹配指定Bean實例內鏈接點,實際上只能使用方法執行做爲鏈接點,定義bean表達式須要傳入id或name,表示只匹配Bean實例內鏈接點,支持「*」通配符。
例如:
//匹配tradeService Bean實例內方法執行的鏈接點
bean(tradeService)
//匹配名字以Service結尾的Bean實例內方法執行的鏈接點。
bean(*Service)
組合切入點表達式:
Spring支持3中邏輯運算符來組合切入點表達式:
&&:要求鏈接點同時匹配兩個切入點表達式;
||:只要鏈接點匹配任意一個切入點表達式;
!:要求鏈接點不匹配指定切入點表達式。
⑤AOP 基於配置Xml文件的管理方式
除了前面介紹的基於JDK1.5的Annotation方式來定義切面、切入點和加強處理,Spring AOP也容許使用XML文件來定義管理它們。
實際上,使用XML定義AOP也是@AspectJ同樣的一樣須要指定相關數據:配置切面、切入點、加強處理所須要的信息徹底同樣,只是提供這些信息的位置不同而已。使用XMLA文件配置AOPd的方式有不少優勢可是也有一些缺點:
xml配置方式比@AspectJ方式有更多的限制:僅支持「singleton」切面的Bean,不能在xml中組合多個命名鏈接點的聲明。
在Spring的配置文件中,全部的切面、切入點和加強處理都必須定義在<aop:config../>元素內部。<beans../>元素下能夠包含多個<aop:config../>元素。
一個<aop:config../>能夠包含多個pointcut、advisor和aspect元素,且這3個元素必須按照此順序類定義。
注意:當咱們使用<aop:config../>方式進行配置時,可能與Spring的自動代理方式衝突,例如咱們使用BeanNameAutoProxyCreator或相似的方式顯示啓用了自動代理,則它可能致使問題(例若有些增請處理沒有被織入)所以要麼所有使用自動代理的方式,要麼所有使用<aop:config../>配置方式。不要不二者混合使用。
————配置切面:
定義切面使用<aop:aspect../>元素,使用該元素來定義切面時,其實質是將一個已有的Spring Bean轉換成切面Bean。
由於切面Bean能夠當成一個普通的SpringBean來配置,因此能夠爲該切面Bean配置依賴注入。
配置<aop:aspect../>元素時能夠指定以下3個屬性:
id:定義該切面的標識名;
ref:指定以指定ref所引用的的普通Bean做爲切面Bean。
order:指定該切面Bean的優先等級,數字越小,等級越大。
————配置加強處理:
<aop:before.../>:Before加強處理
<aop:after../>:After加強處理
<aop:after-returning.../>:afterReturning加強處理
<aop:after-throwing../>:afterThrowing加強處理
<aop:around.../>:Around加強處理
上面的元素不能配置子元素,但能夠配置以下屬性:
pointcut:該屬性指定一個切入點表達式,Spring將在匹配該表達式的鏈接點時織入加強處理。
pointcut-ref:該屬性指定一個已經存在的切入點的 名稱,一般pointcut與pointcut-ref只須要使用其中的一個。
method:該屬性指定一個方法名,指定切面Bean的該方法將做爲加強處理。
throwing:該屬性只對<aop:after-throwing../>起做用,用於指定一個形參名,afterThrowing加強處理方法能夠經過該形參訪問目標方法所拋出的異常。
returning:該屬性只對<aop:after-returning.../>起做用,用於指定一個形參名,afterReturning加強處理方法能夠經過該形參訪問目標方法的返回值。
當定義切入點表達式時,XML文件配置方式和@AspectJ Annotation方式支持徹底相同的切入點指示符,同樣支持execution、within、args、this、target和bean等切入點指示符。
XML配置文件方式和@AspectJ Annotation方式同樣支持組合切入點表達式,但XML配置方式再也不使用簡單的&&、||、!做爲組合運算符,而是使用and(至關於&&)、or(||)和not(!)。
如:
//Spring配置文件:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<aop:config>
<aop:aspectid="fourAdviceAspect"ref="fourAdviceBean"
order="2">
<aop:afterpointcut="execution(* cn.huaxia.spring.lee.*.*(..))"
method="release"/>
<aop:beforepointcut="execution(* cn.huaxia.spring.lee.*.*(..))"
method="authority"/>
<aop:after-returningpointcut="execution(* cn.huaxia.spring.lee.*.*(..))"
method="log"returning="obj"/>
<aop:aroundpointcut="execution(* cn.huaxia.spring.lee.*.*(..))"
method="proceedTX"/>
</aop:aspect>
</aop:config>
<aop:config>
<aop:aspectid="secondAspect"ref="secondAdviceBean"order="1">
<aop:beforepointcut="execution(* cn.huaxia.spring.lee.*.*(..)) and args(aa)"
method="authority"/>
</aop:aspect>
</aop:config>
<!-- 定義一個普通組件Bean -->
<beanid="chinese"class="cn.huaxia.spring.lee.Chinese"/>
<beanid="fourAdviceBean"class="cn.huaxia.spring.lee.FourAdviceTest"/>
<beanid="secondAdviceBean"class="cn.huaxia.spring.lee.SecondAdvice"/>
</beans>
//業務類:
publicclass Chineseimplements Person {
public String sayHello(String word) {
System.out.println("sayHello方法開始執行...");
return word;
}
publicvoid eat(String food) {
System.out.println("我正在吃:" + food);
}
publicvoid divide() {
int a = 5 / 0;
System.out.println("divide執行完畢/" + a);
}
}
//切面Bean:
publicclass FourAdviceTest {
public Object proceedTX(ProceedingJoinPoint pre)throws Throwable {
System.out.println("Around加強處理:執行目標方法前,執行模擬開啓事務.......");
Object[] objs = pre.getArgs();
if (objs !=null && objs.length > 0
&& objs[0].getClass() == String.class) {
objs[0] ="被修改的參數";
}
Object obj = pre.proceed(objs);
System.out.println("Around加強處理:執行目標方法以後,模擬結束事務.....");
return obj +",新增長的內容";
}
publicvoid authority(JoinPoint jp) {
System.out.println("Before加強:模擬權限檢查");
System.out.println("Before加強:被織入加強處理的目標方法:"
+ jp.getSignature().getName());
System.out.println("Before加強:目標方法的參數爲:" + Arrays.toString(jp.getArgs()));
System.out.println("Before加強:被注入加強的處理的目標對象:" + jp.getTarget());
}
publicvoid log(JoinPoint jp, Object obj) {
System.out.println("AfterReturning加強:獲取目標方法的返回值:" + obj);
System.out.println("AfterReturning加強:模擬日誌記錄功能.....");
System.out.println("AfterReturning加強:被織入加強處理的目標方法:"
+ jp.getSignature().getName());
System.out.println("AfterReturning加強:目標方法的參數爲:"
+ Arrays.toString(jp.getArgs()));
System.out.println("AfterReturning加強:被注入加強的處理的目標對象:" + jp.getTarget());
}
publicvoid release(JoinPoint jp) {
System.out.println("After加強:模擬方法結束後,資源釋放.....");
System.out.println("After加強:被織入加強處理的目標方法:"
+ jp.getSignature().getName());
System.out.println("After加強:目標方法的參數爲:" + Arrays.toString(jp.getArgs()));
System.out.println("After加強:被注入加強的處理的目標對象:" + jp.getTarget());
}
}
輸出結果:
————配置切入點:
相似於@AspectJ方式,容許定義切入點來重用切入點表達式,XML配置方式也能夠經過定義切入點來重用切入點表達式。
配置<aop:pointcut../>元素時一般須要指定以下兩個屬性:
id:指定該切入點的標識名;
expression:指定該切入點關聯的切入點表達式。
以下面代碼:
<aop:pointcut id="myPointcut" expression="execution(* lee.*.*(..))"/>
除此以外,若是程序已經使用Annotation定義了切入點,在<aop:pointcut ../>元素中指定切入點表達式時還有另外一種用法:
<aop:pointcut expression="org.huaxia.SystemArchitecture.myPointcut()">
5,Spring的事務:
Spring事務管理不須要與任何特定事務API耦合,對不一樣的持久化層訪問技術,編程式事務提供一致事務編程風格,經過模板化的操做一致性的管理事務。聲明式事務基於Spring AOP實現,並不須要程序開發人員成爲AOP專家,亦能夠輕易使用Spring的聲明式事務管理。
Spring支持的事務策略:
JavaEE應用程序的傳統事務有兩種策略,全局事務和局部事務。全局事務由應用服務器管理,須要底層的服務器的JTA支持,局部事務和底層採用的持久化技術有關,當採用JDBC持久化技術時,須要採用Connection對象來操做事務;而採用Hibernate持久化技術時,須要使用session操做失誤。
當採用傳統的事務編程策略時,程序代碼必然和具體的事務操做代碼耦合。這樣的後果是:當應用程序須要在不一樣的的事務策略之間切換時,開發者必須手動修改程序代碼。當改成Spring操做事務策略時,便可改變這種情況。
Spring事務策略是經過PlatformTransactionManager接口體現的,該接口是Spring事務策略的核心。
即便使用容器管理的JTA,代碼依然不須要JNDI查找,無需與特定的JTA資源耦合在一塊兒,經過配置文件,JTA資源傳給PlatformTransactionManager的實現類,所以,程序的代碼能夠在JTA事務管理和非JTA事務管理之間輕鬆的切換。
Spring是否支持事務跨多個數據庫資源?
答:Spring徹底支持這種跨多個事務性資源的全局事務,前提是底層的應用服務器(如WebLogic、WebSphere等)支持JTA全局事務,能夠這樣說:Spring自己沒有任何事務支持,它只是負責包裝底層的事務————當咱們在程序中面向PlatformTransactionManager接口編程時,Spring在底層負責將這些操做轉換成具體的事務操做代碼,因此應用底層支持什麼樣的事務策略,那麼Spring就支持什麼樣的事務策略。Spring事務管理的優點是將應用從具體的事務API中分離出來,而不是真正提供事務管理的底層實現。
Spring的具體的事務管理由PlatformTransactionManager的不一樣實現類來完成,在Spring容器中配置PlatformTransactionManager時必須針對不一樣的環境提供不一樣的實現類,下面提供了不一樣的持久化訪問環境,及其對應的PlatformTransactionManager實現類的配置。
1,BC數據源的局部事務策略的配置文件以下:
<!-- 定義數據源Bean,使用C3P0數據源實現 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 指定鏈接數據庫的驅動 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<!-- 指定鏈接數據庫的URL -->
<property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
<!-- 指定鏈接數據庫的用戶名 -->
<property name="user" value="root"/>
<!-- 指定鏈接數據庫的密碼 -->
<property name="password" value="32147"/>
<!-- 指定鏈接數據庫鏈接池的最大鏈接數 -->
<property name="maxPoolSize" value="40"/>
<!-- 指定鏈接數據庫鏈接池的最小鏈接數 -->
<property name="minPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的初始化鏈接數 -->
<property name="initialPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的鏈接的最大空閒時間 -->
<property name="maxIdleTime" value="20"/>
</bean>
<!-- 配置JDBC數據源的局部事務管理器,使用DataSourceTransactionManager 類 -->
<!-- 該類實現PlatformTransactionManager接口,是針對採用數據源鏈接的特定實現-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置DataSourceTransactionManager時須要依注入DataSource的引用 -->
<property name="dataSource" ref="dataSource"/>
</bean>
,2,器管理JTA全局事務的配置文件以下:
<!-- 配置JNDI數據源Bean-->
<bean id="dateSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<!--容器管理數據源的JNDI-->
<property name="jndiName" value="jdbc/jpetstore"/>
</bean>
<!--使用JtaTransactionManager類,該類實現PlatformTransactionManager接口-->
<!--針對採用全局事務管理的特定實現-->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
3,採用Hibernate持久層訪問策略時,局部事務的策略的配置文件以下:
<!-- 定義數據源Bean,使用C3P0數據源實現 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 指定鏈接數據庫的驅動 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<!-- 指定鏈接數據庫的URL -->
<property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
<!-- 指定鏈接數據庫的用戶名 -->
<property name="user" value="root"/>
<!-- 指定鏈接數據庫的密碼 -->
<property name="password" value="32147"/>
<!-- 指定鏈接數據庫鏈接池的最大鏈接數 -->
<property name="maxPoolSize" value="40"/>
<!-- 指定鏈接數據庫鏈接池的最小鏈接數 -->
<property name="minPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的初始化鏈接數 -->
<property name="initialPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的鏈接的最大空閒時間 -->
<property name="maxIdleTime" value="20"/>
</bean>
<!-- 定義Hibernate的SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 依賴注入數據源,注入正是上面定義的dataSource -->
<property name="dataSource" ref="dataSource"/>
<!-- mappingResouces屬性用來列出所有映射文件 -->
<property name="mappingResources">
<list>
<!-- 如下用來列出Hibernate映射文件 -->
<value>Person.hbm.xml</value>
</list>
</property>
<!-- 定義Hibernate的SessionFactory的屬性 -->
<property name="hibernateProperties">
<props>
<!-- 指定數據庫方言 -->
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLInnoDBDialect</prop>
<!-- 是否根據須要每次自動建立數據庫 -->
<prop key="hibernate.hbm2ddl.auto">update</prop>
<!-- 顯示Hibernate持久化操做所生成的SQL -->
<prop key="hibernate.show_sql">true</prop>
<!-- 將SQL腳本進行格式化後再輸出 -->
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
<!--配置Hibernate局部事務管理器,使用HibernateTransactionManager類-->
<!--該類是PlatformTransactionManager接口針對採用Hibernate的特定實現類-->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
4,底層採用Hibernate持久化技術,但事務依然採用JTA全局事務:
<!-- 定義數據源Bean,使用C3P0數據源實現 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 指定鏈接數據庫的驅動 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<!-- 指定鏈接數據庫的URL -->
<property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
<!-- 指定鏈接數據庫的用戶名 -->
<property name="user" value="root"/>
<!-- 指定鏈接數據庫的密碼 -->
<property name="password" value="32147"/>
<!-- 指定鏈接數據庫鏈接池的最大鏈接數 -->
<property name="maxPoolSize" value="40"/>
<!-- 指定鏈接數據庫鏈接池的最小鏈接數 -->
<property name="minPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的初始化鏈接數 -->
<property name="initialPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的鏈接的最大空閒時間 -->
<property name="maxIdleTime" value="20"/>
</bean>
<!-- 定義Hibernate的SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 依賴注入數據源,注入正是上面定義的dataSource -->
<property name="dataSource" ref="dataSource"/>
<!-- mappingResouces屬性用來列出所有映射文件 -->
<property name="mappingResources">
<list>
<!-- 如下用來列出Hibernate映射文件 -->
<value>Person.hbm.xml</value>
</list>
</property>
<!-- 定義Hibernate的SessionFactory的屬性 -->
<property name="hibernateProperties">
<props>
<!-- 指定數據庫方言 -->
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLInnoDBDialect</prop>
<!-- 是否根據須要每次自動建立數據庫 -->
<prop key="hibernate.hbm2ddl.auto">update</prop>
<!-- 顯示Hibernate持久化操做所生成的SQL -->
<prop key="hibernate.show_sql">true</prop>
<!-- 將SQL腳本進行格式化後再輸出 -->
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
<!--使用JtaTransactionManager類,該類實現PlatformTransactionManager接口-->
<!--針對採用全局事務管理的特定實現-->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
從上面的配置文件能夠看出,當使用Spring事務管理事務策略時,應用程序無需與具體的事務策略耦合,,Spring提供了兩種事務管理方式:
編程式管理方式:即便使用Spring編程式事務時,程序也可直接獲取容器中的TransactionManager Bean,該Bean老是PlatformTransactionManager的實例,能夠經過該接口提供的3個方法來開始事務,提交事務和回滾事務。
聲明式管理方式:無需在Java程序中書寫任何的事務操做代碼,而是經過在XML文件中爲業務組件配置事務代理(AOP代理的一種),AOP事務代理所織入的加強處理也是由Spring提供:在目標方法前,織入開始事務;在目標方法後執行,織入結束事務。
注意:
Spring編程事務還能夠經過TransactionTemplate類來完成,該類提供了一個execute方法,能夠更簡潔的方式來進行事務操做。
當使用聲明式事務時,開發者無需書寫任何事務管理代碼,不依賴Spring或或任何事務API,Spring的聲明式事務編程無需任何額外的容器支持,Spring容器自己管理聲明式事務,使用聲明式事務策略,可讓開發者更好的專一於業務邏輯的實現。
————使用TransactionProxyFactoryBean建立事務代理————
在Spring1.X,聲明事務使用TransactionProxyFactoryBean來配置事務代理Bean,正如他的名字所暗示的,它是一個工廠Bean,該工程Bean專爲目標Bean生成事務代理Bean,既然TransactionProxyFactoryBean產生的是事務代理Bean,可見Spring的聲明式事務策略是基於Spring AOP的。
例如:
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Spring配置文件的DTD信息 -->
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<!-- Spring配置文件的根元素 -->
<beans>
<!-- 定義數據源Bean,使用C3P0數據源實現 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 指定鏈接數據庫的驅動 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<!-- 指定鏈接數據庫的URL -->
<property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
<!-- 指定鏈接數據庫的用戶名 -->
<property name="user" value="root"/>
<!-- 指定鏈接數據庫的密碼 -->
<property name="password" value="32147"/>
<!-- 指定鏈接數據庫鏈接池的最大鏈接數 -->
<property name="maxPoolSize" value="40"/>
<!-- 指定鏈接數據庫鏈接池的最小鏈接數 -->
<property name="minPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的初始化鏈接數 -->
<property name="initialPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的鏈接的最大空閒時間 -->
<property name="maxIdleTime" value="20"/>
</bean>
<!-- 配置JDBC數據源的局部事務管理器,使用DataSourceTransactionManager 類 -->
<!-- 該類實現PlatformTransactionManager接口,是針對採用數據源鏈接的特定實現-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置DataSourceTransactionManager時須要依注入DataSource的引用 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置一個業務邏輯Bean -->
<bean id="test" class="lee.TestImpl">
<property name="ds" ref="dataSource"/>
</bean>
<!-- 爲業務邏輯Bean配置事務代理 -->
<bean id="testTrans" class=
"org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 爲事務代理工廠Bean注入事務管理器 -->
<property name="transactionManager" ref="transactionManager"/>
<property name="target" ref="test"/>
<!-- 指定事務屬性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
public class TestImpl implements Test
{
private DataSource ds;
public void setDs(DataSource ds)
{
this.ds = ds;
}
public void insert(String u)
{
JdbcTemplate jt = new JdbcTemplate(ds);
jt.execute("insert into mytable values('" + u + "')");
//兩次插入相同的數據,將違反主鍵約束
jt.execute("insert into mytable values('" + u + "')");
//若是增長事務控制,咱們發現第一條記錄也插不進去。
//若是沒有事務控制,則第一條記錄能夠被插入
}
}
配置事務代理的時。須要傳入一個事務管理器,一個目標Bena,並指定該事務的事務屬性,事務實行由transactionAttribute指定,上面只有一條事務傳播規則,該規則指定對於全部方法都使用PROPAGATION_REQUIRED的傳播屬性,Spring支持的事務傳播規則:
PROPAGATION_MANDATORY:要求調用該方法的線程已處於事務環境中,不然拋出異常;
PROPAGATION_NESTED:若是執行該方法的線程已處於事務的環境下,依然啓動新的事務,方法在嵌套的事務裏執行,若是執行該方法的線程並未處於事務的環境下,也啓動新的事務,此時與PROPAGATION_REQUIRED;
PROPAGATION_NOT_SUPPORTED:若是調用該方法的線程處於事務中,則暫停事務,再執行方法。
PROPAGATION_NEVER:不容許執行該方法的線程處於事務的環境下,若是執行該方法的線程處於事務的環境下,則會拋出異常;
PROPAGATION_REQUIRED:要求在事務環境中執行該方法,若是當前執行線程已處於事務中,則直接調用;不然,則啓動新的事務後執行該方法。
PROPAGATION_REQUIREDS_NEW:該方法要求在新的事務中執行,若是當前執行線程已處於事務環境下,則暫停當前事務,啓動新事務後執行方法;不然,啓動新的事務後執行方法。
PROPAGATION_SUPPORTS:若是當前線程處於事務中,則使用當前事務,不然不使用事務。
主程序代碼:
public class MainTest
{
public static void main(String[] args)
{
//建立Spring容器
ApplicationContext ctx = new
ClassPathXmlApplicationContext("bean.xml");
//獲取事務代理Bean
Test t = (Test)ctx.getBean("testTrans");----------①
//執行插入操做
t.insert("bbb");
}
}
上面①處代碼獲取testTrans Bean,該Bean已再也不是TestImpl的實例了,它只是Spring容器建立的事務代理,該事務代理以TestImpl實例爲目標對象,且該目標對象也實現了Test接口,故代理對象也可看成Test實例使用。
實際上,Spring不只支持對接口的代理,整合GBLIB後,Spring甚至能夠對具體類生成代理,只要設置proxyTargetClass屬性爲true就能夠了,若是目標Bean沒有實現任何接口,proxyTargetClass默認爲true,此時Spring會對具體的類生成代理。固然一般建議面向接口編程,而不面向具體的實現類編程。
————Spring2.X的事務配置策略————
上面介紹的TransactionProxyFactoryBean配置策略簡單易懂,可是配置起來極爲繁瑣:每一個目標Bean都須要額外配置一個TransactionProxyFactoryBean代理,這種方法將致使配置文件的急劇增長。
Spring2.X的XML Schema方式提供了更簡潔事務配置策略,Spring提供了tx命名空間來配置事務管理,tx命名空間下提供了<tx:advice../>元素來配置事務切面,一旦使用了該元素配置了切面,就能夠直接使用<aop:advisor.../>元素啓動自動代理。
如:
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Spring配置文件的Schema信息 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!-- 定義數據源Bean,使用C3P0數據源實現 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 指定鏈接數據庫的驅動 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<!-- 指定鏈接數據庫的URL -->
<property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
<!-- 指定鏈接數據庫的用戶名 -->
<property name="user" value="root"/>
<!-- 指定鏈接數據庫的密碼 -->
<property name="password" value="32147"/>
<!-- 指定鏈接數據庫鏈接池的最大鏈接數 -->
<property name="maxPoolSize" value="40"/>
<!-- 指定鏈接數據庫鏈接池的最小鏈接數 -->
<property name="minPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的初始化鏈接數 -->
<property name="initialPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的鏈接的最大空閒時間 -->
<property name="maxIdleTime" value="20"/>
</bean>
<!-- 配置JDBC數據源的局部事務管理器,使用DataSourceTransactionManager 類 -->
<!-- 該類實現PlatformTransactionManager接口,是針對採用數據源鏈接的特定實現-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 配置DataSourceTransactionManager時須要依注入DataSource的引用 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置一個業務邏輯Bean -->
<bean id="test" class="lee.TestImpl">
<property name="ds" ref="dataSource"/>
</bean>
<!-- 配置事務切面Bean,指定事務管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 用於配置詳細的事務語義 -->
<tx:attributes>
<!-- 全部以'get'開頭的方法是read-only的 -->
<tx:method name="get*" read-only="true"/>
<!-- 其餘方法使用默認的事務設置 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 配置一個切入點,匹配lee包下全部以Impl結尾的類
執行的全部方法 -->
<aop:pointcut id="leeService"
expression="execution(* lee.*Impl.*(..))"/>
<!-- 指定在txAdvice切入點應用txAdvice事務切面 -->
<aop:advisor advice-ref="txAdvice"
pointcut-ref="leeService"/>
</aop:config>
</beans>
提示:
Advisor的做用:將Advice和切入點綁在一塊兒,保證Advice所包含的加強處理在對應的切入點被織入。
主程序:
public class MainTest
{
public static void main(String[] args)
{
//建立Spring容器
ApplicationContext ctx = new
ClassPathXmlApplicationContext("bean.xml");
//獲取事務代理Bean
Test t = (Test)ctx.getBean("test");
//執行插入操做
t.insert("bbb");
}
}
上面的配置文件直接獲取容器中的test Bean,由於Spring AOP會爲該Bean自動織入事務加強處理的方式,因此test Bean的全部方法都具備事務性。
配置<tx:advice../>元素時除了須要一個transaction-Manager以外,重要的就是須要配置一個<attributes.../>子元素,該元素又能夠包含多個<method../>子元素。
能夠看出配置<tx:advice../>元素重點就是配置<method.../>子元素,每一個<method../>子元素爲一批方法指定所需的事務語義。
配置<method../>元素時能夠指定以下屬性:
name:必選屬性,與該事務語義關聯的方法名,該屬性支持通配符。
propagation:指定事務傳播行爲,該屬性能夠爲Propagation枚舉類的任意一個枚舉值,該屬性 的默認值是Propagation.REQUIRED;
isolation:指定事務隔離級別,該屬性值可爲Isolation枚舉類的任意枚舉值。
timeout:指定事務超時時間(以秒爲單位),指定-1意味不超時,默認值是-1;
read-only:指定事務是否只讀,該屬性默認爲false;
rollback-for:指定觸發事務回滾的異常類(全名),該屬性能夠指定多個異常類,用英文逗號隔開;
no-rollback-for:指定不觸發事務回滾的類,該屬性能夠指定多個異常類,而且用英文的逗號隔開。
所以咱們能夠爲不一樣的業務邏輯方法指定不一樣的事務策略,如:
<tx:advice id="aaa">
<tx:attributes>
<tx:method name="get*" read--only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<tx:advice id="bbb">
<tx:attributes>
<tx:method name="*" propagation="NEVER"/>
</tx:attributes>
</tx:advice>
6,Spring整合Struts2
①啓動Spring容器:
1,直接在web.xml文件中配置Spring容器;
2,利用第三方MVC框架擴張點,建立Spring容器。
第一種建立Spring容器的方式更加常見,爲了讓Spring容器隨web應用啓動而啓動,有以下兩種方式:
利用ServletContextListener實現;
採用load-on-startup Servlet實現。
Spring提供ServletContextListener的一個實現類ContextLoadListener,該類能夠做爲Listener使用,它會在建立時自動查找WEB-INF目錄下的ApplicationContext.xml,
若是隻有一個配置文件,而且名字爲ApplicationContext.xml,則只須要在web.xml文件添加以下代碼:
<listener>
<listener-class>org.springframework.web.context.ContextLoadListener</listener-class>
</listener>
若是須要加載多個配置文件,則考慮使用<context-param../>元素來肯定配置文件的文件名。ContextLoadListener加載時,會查找名爲contextConfigLocation的初始化參數:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>能夠配置多個文件,用逗號隔開</param-value>
</context-param>
除此以外,Spring提供一個特殊的Servlet類:ContextLoadServlet。該Servlet啓動時,也會自動查找WEB-INF路徑下的ApplicationContext.xml文件。爲了讓ContextLoadServlet隨應用啓動而啓動,應該將此Servlet配置成
load-on-startup的值小一點比較合適,這樣能夠保證ApplicationContext更快的初始化。
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoadServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
該Servlet用於提供後臺服務的,主要用於建立Spring容器的,無需響應客戶端請求,所以無需爲它配<servlet-mapping../>,若是有多個配置文件,或者文件名不是applicationContext,則 同樣使用<context-param.../>元素來肯定多個配置文件。
ContextLoadListener和ContextLoadServlet底層都是依賴於ContextLoad。所以兩者效果沒有什麼區別,他們之間的區別不是它們自己 引發的,而是Servlet規範:Listener比Servlet優先加載,所以採用ContextLoadListener建立ApplicationContext的時機更早。
②MVC框架與Spring整合的思考:
對於一個基於B/S架構的JavaEE而言,用戶請求老是向MVC框架的控制器請求,而當控制器攔截到用戶請求後,必須調用業務組件來處理用戶的請求,那麼控制器如何獲取業務組件呢?
最容易想到的是,直接經過new 關鍵字建立業務邏輯,而後調用該業務邏輯組件的方法。
在實際應用中不多采用上面的訪問策略,至少有一下幾個緣由:
1:控制器直接建立業務組件,致使控制器和業務組件的耦合下降到代碼層次,不利於高層次的解耦;
2:控制器不該該負責業務邏輯組件的建立,控制器只是業務邏輯的使用者,,無需關係業務邏輯組件的實現;
3:每次建立新的業務組件致使性能下降。
答案是採用工廠模式或者服務定位器模式,對於採用服務定位模式,是遠程訪問的場景,在這種情形下,業務組件已經在某個容器中運行,並對外提供某種服務,控制器無需理會該業務組件的建立,直接調用該服務便可,但在調用該服務前,必須先找到該服務。這就是服務定位模式的概念,經典JavaEE應用就是這種結構的應用。
對於輕量級的JavaEE 應用,工廠模式則是更實際的策略,由於在輕量級JavaEE應用中,業務組件不是EJB,一般就是一個POJO,業務組件的生成一般應由工廠負責,並且工廠能夠保證組件的實例只須要一個就夠了,能夠避免重複實例化形成的系統開銷。
若是系統採用Spring框架,則Spring成爲最大的工廠,那麼控制器如何訪問到Spring容器中的業務組件呢?有兩種策略:
1,Spring容器負責管理控制器Action,並利用依賴注入爲容器注入業務組件;
2,利用Spring的自動裝配,Action將會自動從Spring容器中獲取所需的業務邏輯組件。如:
Action類:
publicclass LoginActionimplements Action {
private Stringusername;
private Stringpassword;
private Stringtip;
private MyServicems;
publicString execute()throws Exception {
if(ms.valid(username,password)){
setTip("哈哈哈。整合成功!");
SUCCESS;
}
}
ERROR;
}
業務組件:
publicclass MyServiceImplimplements MyService {
publicboolean valid(String username,String password) {
if(username.equals("johnny")&&password.equals("123")){
returntrue;
}
returnfalse;
}
}
Spring配置文件:
<?xmlversion="1.0"encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<beanid="myService"class="cn.huaxia.user.service.impl.MyServiceImpl"/>
<beanid="loginAction"class="cn.huaxia.web.struts.action.LoginAction"
scope="prototype">
<propertyname="ms"ref="myService"/>
</bean>
</beans>
Struts2配置文件:
<?xmlversion="1.0"encoding="GBK"?>
<!-- 指定Struts2配置文件的DTD信息-->
<!DOCTYPEstrutsPUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<!-- Struts2配置文件的根元素-->
<struts>
<!-- 配置了系列常量-->
<constantname="struts.custom.i18n.resources"value="messageResource"/>
<constantname="struts.i18n.encoding"value="GBK"/>
<packagename="lee"extends="struts-default">
<!-- 定義處理用戶請求的Action,該Action的class屬性不是實際處理類
, 而是Spring容器中的Bean實例-->
<actionname="login"class="loginAction">
<!-- 爲兩個邏輯視圖配置視圖頁面-->
<resultname="error">/error.jsp</result>
<resultname="success">/success.jsp</result>
</action>
<!-- 讓用戶直接訪問該應用時列出全部視圖頁面-->
<actionname="">
<result>.</result>
</action>
</package>
</struts>
7,Spring整合Hibernate
1,Spring提供的dao支持:
DAO模式是標準的java EE設計模式,DAO模式的核心思想是全部的數據庫訪問,都經過DAO組件完成,DAO組件封裝了數據庫的增刪改查等原子操做,業務邏輯組件依賴DAO組件提供的原子操做,
對於javaEE的應用架構,有很是多的,無論細節如何變化,javaEE大體 能夠分爲以下三層:
表現層;
業務邏輯層;
數據持久層;
Spring提供了一系列的抽象類,這些抽象類將被做爲應用中DAO組件能夠經過這些抽象類,Spring簡化了DAO的開發步驟,可以以一致的訪問方式使用數據庫訪問技術,無論底層採用JDBC、JDO仍是個Hibernate,應用中都採用一致的編程模型。
Spring提供的多種數據庫訪問技術的DAO支持,包括Hibernate、JDO、TopLink、iBatis、OJB、JPA等,就Hibernate的持久層訪問技術而言,Spring提供以下3個工具類來支持DAO組件的實現:HibernateDaoSupport、HibernateTemplate、HibernateCallback。
2,管理Hibernate的SessionFactory
前面介紹的Hibernate的時候知道:經過Hibernate進行持久層訪問的時候,Hibernate的SessionFactory是很是重要的對象,它是單個數據庫映射關係編譯後的內存鏡像,大部分狀況下,一個java EE應用對應一個數據庫,即對應一個SessionFactory對象。
配置SessionFactory的示範代碼:
<!-- 定義數據源Bean,使用C3P0數據源實現 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<!-- 指定鏈接數據庫的驅動 -->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<!-- 指定鏈接數據庫的URL -->
<property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
<!-- 指定鏈接數據庫的用戶名 -->
<property name="user" value="root"/>
<!-- 指定鏈接數據庫的密碼 -->
<property name="password" value="32147"/>
<!-- 指定鏈接數據庫鏈接池的最大鏈接數 -->
<property name="maxPoolSize" value="40"/>
<!-- 指定鏈接數據庫鏈接池的最小鏈接數 -->
<property name="minPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的初始化鏈接數 -->
<property name="initialPoolSize" value="1"/>
<!-- 指定鏈接數據庫鏈接池的鏈接的最大空閒時間 -->
<property name="maxIdleTime" value="20"/>
</bean>
<!-- 定義Hibernate的SessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 依賴注入數據源,注入正是上面定義的dataSource -->
<property name="dataSource" ref="dataSource"/>
<!-- mappingResouces屬性用來列出所有映射文件 -->
<property name="mappingResources">
<list>
<!-- 如下用來列出Hibernate映射文件 -->
<value>Person.hbm.xml</value>
</list>
</property>
<!-- 定義Hibernate的SessionFactory的屬性 -->
<property name="hibernateProperties">
<props>
<!-- 指定數據庫方言 -->
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLInnoDBDialect</prop>
<!-- 是否根據須要每次自動建立數據庫 -->
<prop key="hibernate.hbm2ddl.auto">update</prop>
<!-- 顯示Hibernate持久化操做所生成的SQL -->
<prop key="hibernate.show_sql">true</prop>
<!-- 將SQL腳本進行格式化後再輸出 -->
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
3,使用HibernateTemplate:
HibernateTemplate提供持久層訪問模版化,它只須要提供一個SessionFactory的引用,SessionFactory能夠經過構造方法參數傳入,也能夠經過設值方式傳入。
示例:
public class PersonDaoImpl implements PersonDao
{
//定義一個HibernateTemplate對象,用於執行持久化操做
private HibernateTemplate ht = null;
//Hibernate持久化操做所需的SessionFactory
private SessionFactory sessionFactory;
//依賴注入SessionFactory的setter方法
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
//初始化HibernateTemplate的方法
private HibernateTemplate getHibernateTemplate()
{
if (ht == null)
{
ht = new HibernateTemplate(sessionFactory);
}
return ht;
}
/**
* 加載Person實例
* @param id 須要加載的Person實例的標識屬性值
* @return 指定id對應的Person實例
*/
public Person get(Integer id)
{
return (Person)getHibernateTemplate()
.get(Person.class, id);
}
/**
* 保存Person實例
* @param person 須要保存的Person實例
* @return 剛剛保存的Person實例的標識屬性值
*/
public Integer save(Person person)
{
return (Integer)getHibernateTemplate()
.save(person);
}
/**
* 修改Person實例
* @param person 須要修改的Person實例
*/
public void update(Person person)
{
getHibernateTemplate().update(person);
}
/**
* 刪除Person實例
* @param id 須要刪除的Person實例的標識屬性值
*/
public void delete(Integer id)
{
getHibernateTemplate().delete(get(id));
}
/**
* 刪除Person實例
* @param person 須要刪除的Person實例
*/
public void delete(Person person)
{
getHibernateTemplate().delete(person);
}
/**
* 根據用戶名查找Person
* @param name 查詢的人名
* @return 指定用戶名對應的所有Person
*/
public List<Person> findByName(String name)
{
return (List<Person>)getHibernateTemplate()
.find("from Person p where p.name like ?" , name);
}
/**
* 查詢所有Person實例
* @return 所有Person實例
*/
public List findAllPerson()
{
return (List<Person>)getHibernateTemplate()
.find("from Person");
}
/**
* 查詢數據表中Person實例的總數
* @return 數據表中Person實例的總數
*/
public long getPersonNumber()
{
return (Long)getHibernateTemplate().find
("select count(*) from Person as p")
.get(0);
}
}
4,使用HibernateCallback:
HibernateTemplate還提供一種更加靈活的方式來操做數據庫,,經過這種方式能夠徹底使用Hibernate的操做方式,HibernateTemplate的靈活方式是經過以下兩個方法來完成的。
Object execute(HibernateCallback action);
List executeFind(HibernateCallback action);
這兩個方法都須要一個HibernateCallback實例,HibernateCallback是一個接口,該接口包含一個方法doInHibernate(org.hibernate Session session),該方法是有一個Session參數,當咱們在開發中提供HibernateCallback實現類時,必須實現接口裏的doInHibernate方法,該方法體內便可得到 Hibernate Session的引用。一旦獲取到Session的引用咱們就能夠徹底以Hibernate的方式進行數據庫的訪問。
示例:
public class YeekuHibernateDaoSupport extends HibernateDaoSupport
{
/**
* 使用hql語句進行分頁查詢
* @param hql 須要查詢的hql語句
* @param offset 第一條記錄索引
* @param pageSize 每頁須要顯示的記錄數
* @return 當前頁的全部記錄
*/
public List findByPage(final String hql,
final int offset, final int pageSize)
{
//經過一個HibernateCallback對象來執行查詢
List list = getHibernateTemplate()
.executeFind(new HibernateCallback()
{
//實現HibernateCallback接口必須實現的方法
public Object doInHibernate(Session session)
throws HibernateException, SQLException
{
//執行Hibernate分頁查詢
List result = session.createQuery(hql)
.setFirstResult(offset)
.setMaxResults(pageSize)
.list();
return result;
}
});
return list;
}
/**
* 使用hql語句進行分頁查詢
* @param hql 須要查詢的hql語句
* @param value 若是hql有一個參數須要傳入,value就是傳入hql語句的參數
* @param offset 第一條記錄索引
* @param pageSize 每頁須要顯示的記錄數
* @return 當前頁的全部記錄
*/
public List findByPage(final String hql , final Object value ,
final int offset, final int pageSize)
{
//經過一個HibernateCallback對象來執行查詢
List list = getHibernateTemplate()
.executeFind(new HibernateCallback()
{
//實現HibernateCallback接口必須實現的方法
public Object doInHibernate(Session session)
throws HibernateException, SQLException
{
//執行Hibernate分頁查詢
List result = session.createQuery(hql)
//爲hql語句傳入參數
.setParameter(0, value)
.setFirstResult(offset)
.setMaxResults(pageSize)
.list();
return result;
}
});
return list;
}
/**
* 使用hql語句進行分頁查詢
* @param hql 須要查詢的hql語句
* @param values 若是hql有多個個參數須要傳入,values就是傳入hql的參數數組
* @param offset 第一條記錄索引
* @param pageSize 每頁須要顯示的記錄數
* @return 當前頁的全部記錄
*/
public List findByPage(final String hql, final Object[] values,
final int offset, final int pageSize)
{
//經過一個HibernateCallback對象來執行查詢
List list = getHibernateTemplate()
.executeFind(new HibernateCallback()
{
//實現HibernateCallback接口必須實現的方法
public Object doInHibernate(Session session)
throws HibernateException, SQLException
{
//執行Hibernate分頁查詢
Query query = session.createQuery(hql);
//爲hql語句傳入參數
for (int i = 0 ; i < values.length ; i++)
{
query.setParameter( i, values[i]);
}
List result = query.setFirstResult(offset)
.setMaxResults(pageSize)
.list();
return result;
}
});
return list;
}
}
Spring提供了XxxTemplate和XxxCallback互爲補充,XxxTemplate對操做進行封裝,XxxCallback解決了封裝後靈活性不足的缺陷。
5,實現DAO組件:
爲了實現DAO組件,Spring提供了大量的XxxDaoSupport類,這些dao支持類已經完成了大量基礎性的工做。
Spring爲Hibernate的DAO提供工具類:HibernateDaoSupport,該類主要提供以下幾個方法來簡化DAO的實現:
public final HibernateTemplate getHibernateTemplate ();
public final void setSessionFactory(SessionFactory sessionFactory);
setSessionFactory方法可用於接收Spring的依賴注入,容許使用依賴注入Spring 管理的SessionFactory實例。
public class PersonDaoHibernate
extends HibernateDaoSupport implements PersonDao
{
/**
* 加載Person實例
* @param id 須要加載的Person實例的標識屬性值
* @return 指定id對應的Person實例
*/
public Person get(Integer id)
{
return (Person)getHibernateTemplate()
.get(Person.class, id);
}
/**
* 保存Person實例
* @param person 須要保存的Person實例
* @return 剛剛保存的Person實例的標識屬性值
*/
public Integer save(Person person)
{
return (Integer)getHibernateTemplate()
.save(person);
}
/**
* 修改Person實例
* @param person 須要修改的Person實例
*/
public void update(Person person)
{
getHibernateTemplate().update(person);
}
/**
* 刪除Person實例
* @param id 須要刪除的Person實例的標識屬性值
*/
public void delete(Integer id)
{
getHibernateTemplate().delete(get(id));
}
/**
* 刪除Person實例
* @param person 須要刪除的Person實例
*/
public void delete(Person person)
{
getHibernateTemplate().delete(person);
}
/**
* 根據用戶名查找Person
* @param name 查詢的人名
* @return 指定用戶名對應的所有Person
*/
public List<Person> findByName(String name)
{
return (List<Person>)getHibernateTemplate()
.find("from Person p where p.name like ?" , name);
}
/**
* 查詢所有Person實例
* @return 所有Person實例
*/
public List findAllPerson()
{
return (List<Person>)getHibernateTemplate()
.find("from Person");
}
}
實際上,DAO的實現依然藉助於HibernateTemplate的模板訪問方式,只是HibernateDaoSupport提供了兩個工具方法。
至此Java EE應用所需的各類組件都已經出現了。
從用戶的角度上看,用戶發出HTTP請求,當MVC框架的控制器組件攔截到用戶的請求,將調用系統的業務邏輯組件,業務邏輯組件則調用 系統的DAO組件,,而DAO組件則依賴於SessionFactory和DataSOurce等底層的實現數據庫訪問。
從系統的實現角度上看,IoC容器先建立SessionFactory和DataSource等底層組件,而後將這些組件注入DAO組件,提供一個完整的Dao組件,並將DAO組件注入給業務邏輯組件,從而提供一個完整的業務邏輯組件,而業務邏輯組件又被注入給控制器組件,控制器組件負責攔截用戶請求,並將處理結果呈現給用戶。