Spring是一個開放源代碼的設計層面框架,他解決的是業(yè)務邏輯層和其他各層的松耦合問題,因此它將面向接口的編程思想貫穿整個系統(tǒng)應用。Spring是于2003 年興起的一個輕量級的Java 開發(fā)框架,由Rod Johnson創(chuàng)建。簡單來說,Spring是一個分層的JavaSE/EEfull-stack(一站式) 輕量級開源框架。
spring特點
1.方便解耦,簡化開發(fā)
通過Spring提供的IoC容器,我們可以將對象之間的依賴關系交由Spring進行控制,避免硬編碼所造成的過度程序耦合。有了Spring,用戶不必再為單實例模式類、屬性文件解析等這些很底層的需求編寫代碼,可以更專注于上層的應用。
2.AOP編程的支持
通過Spring提供的AOP功能,方便進行面向切面的編程,許多不容易用傳統(tǒng)OOP實現(xiàn)的功能可以通過AOP輕松應付。
3.聲明式事務的支持
在Spring中,我們可以從單調(diào)煩悶的事務管理代碼中解脫出來,通過聲明式方式靈活地進行事務的管理,提高開發(fā)效率和質(zhì)量。
4.方便程序的測試
可以用非容器依賴的編程方式進行幾乎所有的測試工作,在Spring里,測試不再是昂貴的操作,而是隨手可做的事情。例如:Spring對Junit4支持,可以通過注解方便的測試Spring程序。
5.方便集成各種優(yōu)秀框架
Spring不排斥各種優(yōu)秀的開源框架,相反,Spring可以降低各種框架的使用難度,Spring提供了對各種優(yōu)秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
6.降低Java EE API的使用難度
Spring對很多難用的Java EE API(如JDBC,JavaMail,遠程調(diào)用等)提供了一個薄薄的封裝層,通過Spring的簡易封裝,這些Java EE API的使用難度大為降低。
7.Java 源碼是經(jīng)典學習范例
Spring的源碼設計精妙、結(jié)構清晰、匠心獨運,處處體現(xiàn)著大師對Java設計模式靈活運用以及對Java技術的高深造詣。Spring框架源碼無疑是Java技術的最佳實踐范例。如果想在短時間內(nèi)迅速提高自己的Java技術水平和應用開發(fā)水平,學習和研究Spring源碼將會使你收到意想不到的效果。
spring原理機制
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)還是控制反轉(zhuǎn)(Inversion of Conctrol),其含義完全相同:
當某個java實例(調(diào)用者)需要調(diào)用另一個java實例(被調(diào)用者)時,傳統(tǒng)情況下,通過調(diào)用者來創(chuàng)建被調(diào)用者的實例,通常通過new來創(chuàng)建,
而在依賴注入的模式下創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來完成,因此稱之為“控制反轉(zhuǎn)”;創(chuàng)建被調(diào)用者實例的工作通常由Spring來完成,然后注入調(diào)用者,所以也稱之為“依賴注入”。
3,依賴注入一般有2中方式:
設置注入:IoC容器使用屬性的setter方式注入被依賴的實例。《property name=“” ref=“”》
構造注入:IoC容器使用構造器來注入被依賴的實例。《constructor-arg ref=“”》
配置構造注入的時候《constructor-arg》可以配置index屬性,用于指定該構造參數(shù)值作為第幾個構造參數(shù)值。下表從0開始。
4,Spring容器和被管理的bean:
Spring有兩個核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。他們都可以代表Spring容器。
Spring容器是生成Bean實例的工廠,并管理Spring中的bean,bean是Spring中的基本單位,在基于Spring的java EE工程,所有的組件都被當成bean處理。
包括數(shù)據(jù)源、Hibernate的SessionFactory、事務管理器。
①Spring容器:Spring最基本的接口就是BeanFactory,
BeanFactory有很多實現(xiàn)類,通常使用XmlBeanFactory,但是對于大部分的javaEE應用而言,推薦使用ApplictionContext,它是BeanFactory的子接口,
ApplictionContext的實現(xiàn)類為FileSystemXmlApplicationContext和ClassPathXmlApplicationContext
FileSystemXmlApplicationContext:基于文件系統(tǒng)的XML配置文件創(chuàng)建ApplicationContext;
ClassPathXmlApplicationContext:基于類加載路徑下的xml配置文件創(chuàng)建ApplicationContext。
②ApplicationContext的事件機制,
ApplicationContext事件機制是基于觀察者設計模式實現(xiàn)的。通過ApplicationEvent類和ApplicationListener接口,
其中ApplicationEvent:容器事件,必須由ApplicationContext發(fā)布;
ApplicationListener:監(jiān)聽器,可有容器內(nèi)的任何監(jiān)聽器Bean擔任。
③容器中bean的作用域:
singleton:單例模式,在整個Spring IoC容器中,使用singleton定義的bean將只有一個實例;
prototype:原型模式,每次通過容器的getBean方法獲取prototype定義的Bean時,都將產(chǎn)生一個新實例;
request:對于每次HTTP請求中,使用request定義的bean都將產(chǎn)生一個新實例,只有在web應用程序使用Spring時,該作用域才有效;
session:同理
global session:同理
注意:request和session作用域只在web應用中才生效,并且必須在web應用中增加額外的配置才會生效,為了讓request,session兩個作用域生效,必須將HTTP請求對象綁定到為該請求提供服務的線程上,這使得具有request和session作用域的Bean實例能夠在后面的調(diào)用鏈中被訪問。
當支持Servlet2.4及以上規(guī)范的web容器時,我們可以在web應用的web.xml增加如下Listener配置,該Listener負責為request作用域生效:
《listener》
《listener-class》org.springframework.web.context.request.RequestContextListener《/listener-class》
《/listener》
如果僅使用了支持Servlet2.4以前規(guī)范的web容器,則該容器不支持Listener規(guī)范,故無法使用這種配置,可以使用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=“l(fā)ee.Person” scope=“request”/》
這樣容器就會為每次HTTP請求生成一個lee.Person的實例當該請求響應結(jié)束時,該實例也隨之消失。
如果Web應用直接使用Spring MVC作為MVC框架,即使用SpringDispatchServlet或DispatchPortlet來攔截所有用戶請求,則無需這些額外的配置,因為SpringDispatchServlet或DispatchPortlet已經(jīng)處理了所有和請求有關的狀態(tài)處理。
④獲取容器的引用:
通常情況下:
Bean無需訪問Spring容器,而是通過Spring容器訪問的,即使 需要手動訪問Spring容器,程序也已通過類似下面的代碼獲取Spring容器 的引用。
ApllicationContext cts = ClassPathApplalicationContext(“bean.xml”);
但在一些極端的情況下,可能Bean需要訪問Spring容器。Spring提供另一種方法訪問Spring容器:
實現(xiàn)BeanFactoryAware接口的Bean,擁有訪問Spring容器的能力,實現(xiàn)BeanFactoryAware的Bean被容器實例化后,會擁有一個引用指向創(chuàng)建他的BeanFactory。BeanFactoryAware只有一個方法setBeanFactory(BeanFactory beanFactory)該參數(shù)指向創(chuàng)建他的BeanFactory。
缺點:污染了代碼,使代碼與Spring接口耦合在一起,因此沒有特別的必要,建議不要直接訪問容器。
5,Bean實例的創(chuàng)建方式及對應配置:
創(chuàng)建Bean的方法:
①調(diào)用構造器創(chuàng)建Bean實例;
②調(diào)用靜態(tài)工廠方法創(chuàng)建Bean;
③調(diào)用實例工廠創(chuàng)建Bean。
調(diào)用靜態(tài)工廠方法創(chuàng)建Bean:
class屬性是必須的,但此時的class并不是指定Bean實例的實現(xiàn)類而是靜態(tài)工廠類。采用靜態(tài)工廠類需要配置如下兩個屬性:
class靜態(tài)工廠類的名字;
factory-method工廠方法(必須是靜態(tài)的)。
如果靜態(tài)工廠的方法有參數(shù)通過《constructor-arg/》元素知道。
調(diào)用實例工廠方法創(chuàng)建Bean:
使用實例工廠Bean時class屬性無需指定,因Spring容器不會直接實例化該Bean,
創(chuàng)建Bean時需要如下屬性:
factory-bean:該屬性為工廠Bean的ID;
factory-method:該屬性是定實例工廠的工廠方法。
6,入理解Spring容器中的Bean:
抽象Bean:
所有的抽象Bean,就是是定abstract屬性為true的Bean,抽象Bean不能被實例化,抽象Bean的價值在于被繼承
使用子Bean:
隨著應用規(guī)模的增大,Spring配置文件的增長速度更快。當應用中的組件越來越多,,Spring中的Bean配置也隨之大幅度增加。
就會出現(xiàn)一中現(xiàn)象:有一批配置Bean的信息完全相同,只有少量 的配置不同。怎么解決呢?
這時候就可以用Bean的繼承來解決。
注意:子Bean無法從父Bean繼承如下屬性:
depends-on,aotuwirwe,dependency-check,singleton,scope,lazy-iniyt這些屬性總是子Bean定義,或采用默認值。
通過 為一個《bean.。。/》 元素指定parent屬性,即可指定該Bean是一個子Bean。
Bean繼承與java中繼承的區(qū)別:
Spring中的子bean和父Bean可以是不同類型,但java中的繼承則可保證子類是一種特殊的父類;
Spring中的Bean的繼承是實例之間的關系,因此只要表現(xiàn)在參數(shù)值的延續(xù),而java中的繼承是類之間的關系,主要表現(xiàn)為方法、屬性之間的延續(xù);
Spring中的子Bean不可以作為父Bean使用,不具備多態(tài)性,java中的子類完全可以當成父類使用。
Bean的生命周期:
①singleton與prototype的區(qū)別:
singleton:Spring可以精確的知道該Bean何時被創(chuàng)建、初始化、銷毀。對于singleton作用域的Bean,每次客戶端請求Spring容器總會返回一個共享的實例。
prototype:Spring容器僅僅負責創(chuàng)建Bean,當容器創(chuàng)建了Bean的實例后,Bean實例完全交給客戶端代碼管理,容器不在跟蹤其生命周期。
每次客戶端請求prototype作用域的Bean,都會為他創(chuàng)建一個新的實例,
②依賴關系注入后的行為:
Spring提供兩種方法在Bean全部屬性設置成功后執(zhí)行特定的行為:
使用init-method屬性;
該Bean實現(xiàn)InitializingBean接口
第一種方法:使用init-method屬性指定某個方法在Bean全部屬性依賴關系設置結(jié)束后自動執(zhí)行。使用這種方法不需要將代碼與Spring的接口耦合在一起,代碼污染少;
第二種方法:實現(xiàn)Initializing接口,該接口有一個方法void afterPropertiesSet() throws Exception,雖然實現(xiàn)次接口一樣可以在Bean全部屬性設置成功后執(zhí)行特定的行為,但是污染了代碼,是侵入式設計,因此不推薦使用。
注意:如果即采用init-method屬性指定初始化方法,又實現(xiàn)InitializingBean接口來指定初始化方法,先執(zhí)行initializingBean接口中定義的方法,再執(zhí)行init-method屬性指定的方法。
③Bean銷毀之前行為:
與定制初始化相似,Spring也提供兩種方式定制Bean實例銷毀之前的特定行為,如下:
使用destroy-method屬性:
實現(xiàn)DisposableBean接口:
注意:如果即采用destroy-method屬性指定銷毀之前的方法,又實現(xiàn)DisposableBean接口來指定指定銷毀之前的方法,與②類似。
④default-init-method與default-destroy-method屬性,指定了所有的Bean都會執(zhí)行此方法,而不是單個的Bean。
協(xié)調(diào)作用域不同步的Bean:
描述:
當Spring容器中作用域不同的Bean相互依賴時,可能出現(xiàn)一些問題:
當兩個singleton作用域Bean存在依賴關系時,或當prototype作用依賴singleton作用域的Bean時,通過屬性定義依賴關系即可。、
但是,當singleton作用域的Bean依賴prototype作用域Bean時,singleton作用域的Bean只有一次初始化的機會,他的依賴關系也只有在初始化階段被設置,而他所依賴的prototype作用域的Bean則會不斷的產(chǎn)生新的Bean實例。
解決方案:
第一種:部分放棄依賴注入:singleton作用域的Bean每次需要prototype作用域的Bean,則主動向容器請求新的Bean實例。
第二種:利用方法注入。
第一種方案肯定是不好的,代碼主動請求新的Bean實例,必然會導致與Spring API耦合,造成代碼嚴重污染。
通常情況下采用第二中方式。
方法注入通常使用lookup方法注入,利用lookup方法注入可以讓Spring容器重寫容器中Bean的抽象方法或具體方法,返回查找容器中的其他 Bean,被查找的Bean通常是non-singleton Bean(盡管也可以是singleton)。
如:public class SteelAxe implements Axe{
//每執(zhí)行一次加一
private int count;
public String chop(){
return ++count;
}
}
public abstract class Chinese implements Perosom{
private Axe axe;
//定義一個抽象方法,該方法將由Spring負責實現(xiàn)
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與前面介紹的實例工廠方法創(chuàng)建Bean、靜態(tài)工廠創(chuàng)建Bean有所區(qū)別:
前面的那些工廠是標準的工廠模式,Spring只是負責調(diào)用工廠方法來創(chuàng)建Bean實例;
此處工廠Bean是Spring的一種特殊Bean,這種工廠Bean必須實現(xiàn)FactoryBean接口。
FactoryBean接口是工廠Bean標準的工廠Bean的接口,實現(xiàn)該接口的Bean只能當工廠Bean使用,當我們將工廠Bean部署在容器中,并通過getBean()方法來獲取工廠Bean,容器不會返回FactoryBean實例而是FactoryBean的產(chǎn)品。
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創(chuàng)建Reource對象
ClassPathResource re = new ClasspathResource(“bean.xml”);
//創(chuàng)建BeanFactory
XmlBeanFactory factory = new XmlBeanFactory(re);
Person p = (Person)factory.getBean(“chinese”);
//如需要獲取FactoryBean本身則應該在bean id前加&
Person p = (Person)factory.getBean(“&chinese”);
}
對于初學者可能無法體會到工廠bean的作用,實際上,F(xiàn)actoryBean是Spring中非常有用的接口。例如:TransationProxyFactroyBean,這個工廠轉(zhuǎn)為目標Bean創(chuàng)建事務代理。
7,深入理解依賴關系配置
組件與組件之間的耦合,采用依賴注入管理,但是普通的javabean屬性值,應直接在代碼里設置。
對于singleton作用域的bean,如果沒有強制取消其預初始化行為,系統(tǒng)會在創(chuàng)建Spring容器時預初始化所有的singleton作用域的bean,與此同時,該bean依賴的bean也一起被實例化。
BeanFactory與ApplicationContext實例化容器中的bean的時機不同,前者等到程序需要Bean實例才創(chuàng)建Bean
后者會預初始化容器中的所有Bean。
因為采用ApplicationContext作為Spring的容器,創(chuàng)建容器時,會創(chuàng)建容器中所有singleton作用域的所有bean,因此可能需要更多的系統(tǒng)資源,但是一旦創(chuàng)建成功。應用后面的 響應速度會很快,因此,對于普通的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=“l(fā)ee.ValueGenrator”/》
《bean id=“son1” class=“Son”》
《property name=“age”》
《bean class=“org.springframework.bean.factory.congfig.MethodInvokignFactoryBean”》
//配置非靜態(tài)方法
《property name=“targetObject” ref=“valueGenrator”/》
//配置靜態(tài)方法
《!--
《property name=“targetClass” value=“l(fā)ee.ValueGenrator”/》
--》
《property name=“targetMehtod” value=“getStaticValue/》
《/property》
《/bean》
8,強制初始化Bean:
Spring有一個默認的規(guī)則,總是先初始化主調(diào)Bean,然后在初始化依賴Bean。
為了指定Bean在目標Bean之前初始化,可以使用depends-on屬性
9,自動裝配:
Spring能自動裝配Bean與Bean之間的依賴關系,即使無需使用ref顯式指定依賴Bean。
Spring的自動裝配使用autowire屬性指定,每一個《bean/》元素都可以指定autowire屬性,也就是說在Spring容器中完全可以讓某些Bean自動裝配,而某些Bean不沒使用自動裝配。
自動裝配可以減少配置文件的工作量,但是降低了依賴關系的透明性和依賴性。
使用autowire屬性自動裝配,autowire屬性可以接受如下幾個值 :
no:不使用自動裝配。這是默認配置。
byName:根據(jù)屬性自動裝配,BeanFactory會查找容器中所有的Bean,找出id屬性與屬性名同名的Bean來完成注入。如果沒有找到匹配的Bean,Spring則不會進行任何注入。
byType:根據(jù)屬性類型自動裝配,BeanFactroy會查找容器中所有的 Bean,如果一個正好與依賴屬性類型相同的Bean,就會自動注入這個屬性。
如果有多個這樣的Bean則會拋出異常。如果沒有這樣 的Bean則什么也不會發(fā)生,屬性不會被設置。
constructor:與byType類似,區(qū)別是用于構造注入的參數(shù),如果BeanFactory中不是恰好有一個Bean與構造器參數(shù)類型相同。則會拋出異常。
autodetect:BeanFactory會根據(jù)Bean內(nèi)部的結(jié)構,決定使用constructor或byType,如果找到一個缺省的構造器,就會應用byType。
注意:對于大型的應用而言,不鼓勵使用自動裝配,
10,依賴檢查:
Spring提供一種依賴檢查的功能,可以防止出現(xiàn)配置手誤,或者其他情況的錯誤。
使用依賴檢查可以讓系統(tǒng)判斷配置文件的依賴關系注入是否完全有效。
使用依賴檢查,可以保證Bean的屬性得到了正確的設置,有時候,某個Bean的特定屬性并不需要設置值,或者某些屬性已有默認值,此時采用依賴檢查就會出現(xiàn)錯誤,該Bean就不應該采用依賴檢查,幸好Spring可以為不同的Bean單獨指定依賴檢查的行為,Spring提供dependency-chech屬性來配置依賴檢查,當然也可以指定不同的檢查依賴策略。
該屬性有如下值:
none:不進行依賴檢查,沒有指定值的Bean屬性僅僅是沒有設置值,這是默認值。
simple:對基本類型和集合(除了合作者Bean)進行依賴檢查。
objects:僅對合作者Bean進行依賴檢查。
all:對合作者Bean、基本數(shù)據(jù)類型全部進行依賴檢查。
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》
以上程序?qū)伋霎惓#m然Chinese類的age屬性已經(jīng)有了默認值,但配置dependency-check=”all“則要求配置文件為所有的屬性都提供正確的值。
而age屬性沒有提供值。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二:深入Spring:
1,利用后處理器擴展Spring容器:
Spring容器提供了很好的擴展性,除了可以與各種第三方框架良好的整合外,其IoC容器也允許開發(fā)者進行擴展,這種擴展甚至無需實現(xiàn)BeanFactory或ApplicationContext接口,允許兩個后處理器對IoC容器進行擴展:
Bean后處理器:這種處理器會對容器中的Bean進行后處理,對Bean的功能進行額外的加強。
容器后處理器:這種處理器對IoC容器進行后處理,用于增強容器的功能。
①Bean后處理器:
Bean后處理器是一種特殊的Bean,這種特殊的Bean并不對外提供服務,它甚至可以沒有id屬性。它主要負責對容器中的其他Bean執(zhí)行后處理。
后處理器必須實現(xiàn)BeanPostProcessor接口。此接口有兩個方法:Object postProcessorBeforeInitialization(Object object,String name) Object postProcessorAfterInitialization(Object object,String name)
第一個參數(shù)是系統(tǒng)即將后處理的Bean的實例,第二個參數(shù)是該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執(zhí)行注入。。.。。.。“);
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后處理器發(fā)生作用。
輸出:
---------------------------------------------------------------
Spring執(zhí)行注入依賴關系。。.。。.
后處理器在初始化之前對chinese進行增強處理
初始化工作init
后處理器在初始化之后對chinese進行增強處理
張三
----------------------------------------------------------------
如果使用BeanFactory作為Spring的容器。則必須手動注冊Bean后處理器,容器中一旦注冊了Bean后處理器,Bean后處理器就會自動啟動,在容器中每個Bean創(chuàng)建時自動工作。
實現(xiàn)BeanPostProcessor接口的Bean后處理器可以對Bean進行任何操作,包括完全忽略這個回調(diào),BeanPostProcessor通常用來檢查標記接口,或者做將Bean包裝成一個Proxy的事情。
如果不采用BeanFactory作為Spring的容器,采用ApplicationContext作為容器,則無需手動注冊Bean后處理器,ApplicationContext可自動檢測到容器中的Bean后處理器,自動注冊。Bean后處理器會在創(chuàng)建Bean時自動啟動。對于后處理器而言,ApplicationContext作為Spring容器更為方便。
②容器后處理器:
除了上面的提供的Bean后處理器外,Spring還提供了一種容器后處理器,,Bean后處理器負責容器中所有Bean實例的,而容器后處理器負責處理容器本身。
容器后處理器必須要實現(xiàn)BeanFactoryPostProcessor接口。
類似于Bean后處理器,采用ApplicationContext作為容器,則無需手動注冊容器后處理器。如果使用BeanFactory作為Spring的容器。則必須手動注冊容器后處理器。
2,Spring的“零配置”支持
①搜索Bean類
Spring提供如下幾個Annotation來標注Spring Bean:
@Component標注一個普通的Spring Bean;
@Controller:標注一個控制器組件類;
@Service:標注一個業(yè)務邏輯組件類;
@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類型匹配,則會出現(xiàn)異常。
當@Autowired標注數(shù)組類時,在這種情況,Spring會自動搜索Spring容器中所有與數(shù)組類型相匹配的類型的Bean,并把這些Bean當作數(shù)組的元素來創(chuàng)建數(shù)組。
當@Autowired標注集合時,和標注數(shù)組類似,當時必須使用泛型。
⑤使用@Qualifier精確裝配
正如上面的@Autowired總是采用byType的自動裝配策略。在這種情況下,符合自動和裝配的類型的Bean常常有多個這個時候就會引發(fā)異常了。
為了實現(xiàn)精確的配置,Spring提供@Qualifier,可以根據(jù)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):業(yè)務流程運行到某個特定的步驟,也就是一個應用運行的關注點,關注點可能橫切多個對象,所以常常也被稱為橫切關注點。
連接點(Joinpoint):程序執(zhí)行過程明確的點,如方法的調(diào)用、異常的拋出,Spring AOP中連接點總是方法的調(diào)用。
增強處理(Advice):AOP框架在特定的切入點執(zhí)行的增強處理,處理有around、before和After等類型。
切入點(Pointcut):可以插入增強處理的連接點,簡而言之,當某個連接點滿足指定要求時,該連接點將被添加增強處理,該連接點也就變成了切入點,
例如一下代碼:
pointcut xxxPointcut(){
:execution(void H*.say*());
}
每個方法的調(diào)用都只是連接點,但如果該方法屬于H開頭的類,且方法名以say開頭,按照方法的執(zhí)行將變成切入點。
引入:將方法添加到被處理的類中,Spring允許引入新的接口到任何被處理的對象。例如:你可以使用一個引用,使任何對象實現(xiàn)IsModified接口,以此簡化緩存。
目標對象:被AOP增強處理的對象,也被稱為被增強的對象,如果AOP框架是通過運行時代理來實現(xiàn)的,那么這個對象將是一個被代理的對象。
AOP代理:AOP框架創(chuàng)建的 對象,簡單的說,代理就是對目標對象的加強,Spring的AOP代理可以使JDK動態(tài)代理,也可以是CGLIB代理,前者為實現(xiàn)接口的目標對象的代理后者為不實現(xiàn)接口的目標對象的代理。
織入(Weaving):將增強處理增加到目標對象中,并創(chuàng)建一個被增強的對象(AOP代理)的過程就是織入。織入有兩種實現(xiàn)方式:編譯時增強(例如AspectJ)和運行時增強(例如CGLIB)
目前Spring只支持將方法調(diào)用作為連接點,如果需要把Field的訪問和更新也作為增強處理的連接點,可以使用AspectJ。
一旦我們掌握了上面的AOP的相關概念,不難發(fā)現(xiàn)進行AOP的編程其實是一件很簡單的事情,縱觀AOP編程需要程序員參與的只有3個部分:
定義普通業(yè)務組件;
定義切入點,一個切入點橫切多個業(yè)務組件;
定義增強處理,增強處理就是現(xiàn)在AOP框架為普通業(yè)務組件織入的處理動作。
③AOP,基于Annotation的零配置方式:
定義Before增強處理:
當我們在一個切面類里使用@Before來標注一個方法時,該方法將作為Before增強處理,使用@Before標注時通常要指定一個value屬性值,用來指定一個切入點表達式(既可以是一個已有的切入點,也可以直接定義切入點表達式),用于指定該增強處理將被織入那些切入點。
注意:(”execution(* cn.huaxia.spring.*.*(。。))“)第一個星號后面一定要有一個空格。
//定義一個切面
@Aspect
publicclass BeforeAdviceTest {
// 執(zhí)行cn.huaxia.spring包下的所有方法都做為切入點
@Before(”execution(* cn.huaxia.spring.*.*(。。))“)
publicvoid authority() {
System.out.println(”模擬執(zhí)行權限檢查。。.“);
}
}
上面@Aspect@標注BeforeAdviceTest 表明該類是一個切面類,在該切面里定義了一個authority()方法,這個方法本來沒有什么特別之處,但是因為使用@Before來標注該方法,這就將該方法轉(zhuǎn)換成一個增強處理。上面程序中使用@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“);//會出錯,不能轉(zhuǎn)換為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有參數(shù),returning屬性一定要配置。如果參數(shù)不止一個,那么目標方法將不會執(zhí)行必須要配置args表達式。
注意:使用returning屬性還有另一個額外的作用:它可以限定切入點只匹配具有對應返回類型的方法。加入上面的log的參數(shù)是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方法開始執(zhí)行。。.“);
new FileInputStream(”a.txt“);
}catch (FileNotFoundException e) {
e.printStackTrace();
}
return word;
}
publicvoid eat(String food) {
System.out.println(”我正在吃“ + food);
}
publicvoid divide(){
int a = 5/0;
System.out.println(”divide執(zhí)行完畢/“+a);
}
}
//Chinese c = (Chinese)ac.getBean(”chinese“);//會出錯,不能轉(zhuǎn)換為Chinese
Person c = (Person)ac.getBean(”chinese“);
System.out.println(c.sayHello(”你好--Before“));
c.eat(”西瓜“);
c.divide();
}
輸出的結(jié)果:
上面的程序的sayHello和divided兩個方法都會拋出異常,但是sayHello方法中的異常將由該方法顯示捕捉,所以Spring AOP不會處理該異常。
而divide方法將拋出算術異常,且該異常沒有被任何程序所處理,故Spring AOP會對該異常進行處理。
使用throwing屬性還有另一個作用:它可以用于限定切入點只匹配指定類型的異常。類似AfterRetuning的returning 屬性。
Spring AOP的AfterThrowing處理雖然可以對目標方法的異常進行處理,但是這種處理與直接使用catch捕捉是不同的:
catch意味完全處理該異常,如果catch沒有重新拋出一次異常,則方法可以正常結(jié)束,而AfterThrowing處理雖然處理了異常,但它不能完全處理異常,該異常依然會傳播到上一級調(diào)用者。
定義After增強處理:
After增強處理與AfterReturning增強處理有點相似,但是也有不同:
AfterRetuning只在目標方法成功完成后才被織入。
After增強處理不管目標方法如何結(jié)束,都會被織入。
因為一個方法不管是如何結(jié)束,After增強處理它都會被織入,因此After增強處理必須處理正常返回和異常返回兩種情況,這種增強處理通常用于資源釋放。
使用@After Annotation標注一個方法,即可將該方法轉(zhuǎn)成After增強處理,使用@After Annotation增強處理需要指定一個value屬性,該屬性值用于指定該增強處理被織入的切入點,既可以是一個已有的切入點也可以直接指定切入點表達式。
@Aspect
publicclass AfterAdviceTest {
@After(”execution(* cn.huaxia.spring.*.*(。。))“)
publicvoid release(){
System.out.println(”模擬方法結(jié)束后資源釋放。。.。“);
}
}
結(jié)果:
從上面的輸出結(jié)果可以看出,After增強處理非常類似于異常處理中的finally塊的作用-----無論結(jié)果如何,它總在方法執(zhí)行結(jié)束之后被織入,因此特別適合進行資源回收。
Around增強處理:
@Around Annotation用于標注Around的增強處理,Around的增強處理是比較強的增強處理,它近似等于Before和AfterRetuning增強處理的總和,Around增強處理既可以在目標方法之前執(zhí)行,也可以在執(zhí)行目標方法之后織入增強動作。
與Before增強處理和AfterReturning增強處理不同的是Around增強處理可以決定目標方法執(zhí)行在什么時候執(zhí)行,如何執(zhí)行,甚至可以阻止目標方法執(zhí)行。Around增強處理可以改變執(zhí)行目標方法的參數(shù)值,還可以改變執(zhí)行目標方法之后的返回值。
使用@Around Annotation時需要指定value屬性,該屬性用來指定該增強處理被織入的切入點。
當定義一個Around增強處理方法時,該方法的第一個形參必須是ProceedingJoinPoint類型(至少包含一個形參)。
在增強處理方法體內(nèi),調(diào)用ProceedingJoinPoint的process方法時,還可以傳入一個Object[]對象,該數(shù)組中的值將被傳入目標方法作為執(zhí)行方法的的實參。
-------這就是Around增強處理可以完全控制目標方法執(zhí)行時機、如何執(zhí)行的關鍵。如果程序沒有調(diào)用ProceedingJoinPoint的proceed方法則目標方法不會執(zhí)行。
@Aspect
publicclass AroundAdviceTest {
@Around(”execution(* cn.huaxia.spring.*.*(。。))“)
public Object proceedTX(ProceedingJoinPoint pre)throws Throwable {
System.out.println(”開始事務。。.。。“);
Object obj = pre.proceed(new String[] {”被修改的參數(shù)“ });
System.out.println(”結(jié)束事務。。.。。“);
return obj +”,新增的內(nèi)容“;
}
}
結(jié)果:
當調(diào)用ProceedingJoinPonint的proceed方法時,傳入的Object[]參數(shù)值將作為目標方法的參數(shù),如果傳入的Object[]參數(shù)的長度和目標方法參數(shù)個數(shù)不匹配,或者Object[]數(shù)組元素與目標方法所需的參數(shù)不匹配,程序就會拋出異常。
為了能獲取目標方法參數(shù)的個數(shù)和類型,這就需要增強處理方法能夠訪問執(zhí)行目標方法的參數(shù)了。
訪問目標方法的參數(shù):
訪問目標方法最簡單的的做法是定義增強處理方法時將第一個參數(shù)定義為JoinPoint類型,當增強處理方法被調(diào)用時,該JoinPoint參數(shù)就代表了織入增強處理的連接點,JoinPoint包含如下幾個常用的方法:
Object[] getArgs():返回執(zhí)行目標方法的參數(shù);
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增強處理:執(zhí)行目標方法前,執(zhí)行模擬開啟事務。。.。。.。“);
Object[] objs = pre.getArgs();
if (objs != null && objs.length 》 0
&& objs[0].getClass() == String.class) {
objs[0] = ”被修改的參數(shù)“;
}
Object obj = pre.proceed(objs);
System.out.println(”Around增強處理:執(zhí)行目標方法之后,模擬結(jié)束事務。。.。。“);
return obj + ”,新增加的內(nèi)容“;
}
@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增強:目標方法的參數(shù)為:“ + 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增強:目標方法的參數(shù)為:“
+ 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增強:模擬方法結(jié)束后,資源釋放。。.。。“);
System.out.println(”After增強:被織入增強處理的目標方法:“
+ jp.getSignature().getName());
System.out.println(”After增強:目標方法的參數(shù)為:“ + Arrays.toString(jp.getArgs()));
System.out.println(”After增強:被注入增強的處理 的目標對象:“ + jp.getTarget());
}
}
輸出結(jié)果:
Around增強處理:執(zhí)行目標方法之后,模擬結(jié)束事務。。.。。
AfterReturning增強:獲取目標方法的返回值:被修改的參數(shù),新增加的內(nèi)容
AfterReturning增強:模擬日志記錄功能。。.。。
AfterReturning增強:被織入增強處理的目標方法:sayHello
AfterReturning增強:目標方法的參數(shù)為:[被修改的參數(shù)]
AfterReturning增強:被注入增強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
After增強:模擬方法結(jié)束后,資源釋放。。.。。
After增強:被織入增強處理的目標方法:sayHello
After增強:目標方法的參數(shù)為:[被修改的參數(shù)]
After增強:被注入增強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
被修改的參數(shù),新增加的內(nèi)容
Around增強處理:執(zhí)行目標方法前,執(zhí)行模擬開啟事務。。.。。.。
Before增強:模擬權限檢查
Before增強:被織入增強處理的目標方法:eat
Before增強:目標方法的參數(shù)為:[被修改的參數(shù)]
Before增強:被注入增強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
我正在吃:被修改的參數(shù)
Around增強處理:執(zhí)行目標方法之后,模擬結(jié)束事務。。.。。
AfterReturning增強:獲取目標方法的返回值:null,新增加的內(nèi)容
AfterReturning增強:模擬日志記錄功能。。.。。
AfterReturning增強:被織入增強處理的目標方法:eat
AfterReturning增強:目標方法的參數(shù)為:[被修改的參數(shù)]
AfterReturning增強:被注入增強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
After增強:模擬方法結(jié)束后,資源釋放。。.。。
After增強:被織入增強處理的目標方法:eat
After增強:目標方法的參數(shù)為:[被修改的參數(shù)]
After增強:被注入增強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
Around增強處理:執(zhí)行目標方法前,執(zhí)行模擬開啟事務。。.。。.。
Before增強:模擬權限檢查
Before增強:被織入增強處理的目標方法:divide
Before增強:目標方法的參數(shù)為:[]
Before增強:被注入增強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
After增強:模擬方法結(jié)束后,資源釋放。。.。。
After增強:被織入增強處理的目標方法:divide
After增強:目標方法的參數(shù)為:[]
After增強:被注入增強的處理的目標對象:cn.huaxia.spring.service.Chinese2@941db6
Spring AOP采用和AsoectJ一樣的優(yōu)先順序來織入增強:在”進入“連接點時,最高等級的增強處理將先織入(所以給定兩個Before增強處理中,優(yōu)先級高的那個會執(zhí)行),在”退出“連接點時,最高優(yōu)先等級的增強處理將會最后織入。
4個增強處理的優(yōu)先等級如下(從低到高):
Before增強處理-------------》Around增強處理-------------》AfterReturning增強處理-------------》After增強處理
當兩個不同的兩個增強處理需要在同一個連接點被織入時,Spring AOP將以隨機的順序來織入這兩個增強處理,如果應用需要指定不同切面里增強處理的優(yōu)先級,Spring提供了兩個解決方案:
讓切面類實現(xiàn)org.springframework.core.Ordered接口,實現(xiàn)該接口提供的 int getOrder()方法,方法返回值越小,則優(yōu)先級越高。
直接使用@Order Annotation 來標注一個切面類,使用@Order Annotation時可以指定一個int 型的value屬性,該屬性值越小,優(yōu)先等級越高。
當相同的切面里的兩個增強處理需要在相同的連接點被織入時,Spring AOP將以隨機的方式來織入這兩個增強處理,沒有辦法指定他們的順序,如果一定要它們以特定的順序被織入,則可以考慮把它們壓縮到一個增強處理中,或者是把它們分別放在不同的切面,在通過切面的優(yōu)先等級來排序
如果只要訪問目標方法的參數(shù),Spring還提供了一個更加簡單的方式:我們可以在程序中使用args來綁定目標方法的參數(shù),如果args表達是中指定一個或多個參數(shù),則該切入點只匹配具有對應形參的方法,且目標方法的參數(shù)值將被出入增強處理方法。
如:
@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參數(shù)“ + food);
System.out.println(”目標方法的time參數(shù)“ + time);
System.out.println(”模擬日志記錄功能。。.。。.“);
System.out.println(”目標參數(shù)的返回值:“+retValue);
}
}
@Component
publicclass Chinese3implements Person2 {
@Override
public String sayHello(String word) {
return word;
}
publicvoid eat(String food, Date time) {
System.out.println(”我正在吃:“ + food+”,現(xiàn)在的時間是:“ + time);
}
}
結(jié)果:
為什么目標方法的返回值是null,因為該切入點只匹配 publicvoid eat(String food, Date time)方法。
從上面可以得出,使用args表達式有如下兩個作用:
--提供更簡單的方式訪問目標方法的參數(shù);
--可用于對切入表達式增加額外限制。
除此之外,使用args表達式時還可以使用如下形式:args(name,age,。。),這表明增強處理方法中可通過name,age來訪問目標方法的參數(shù),上面表達式括號中的2點,它表示可匹配更多的參數(shù),如:
public void doSomething(String name,int age)
這意味著只要目標方法第一個參數(shù)是String類型,第二個參數(shù)是int則該方法就可以匹配該切入點。
定義切入點:
正如前面的FourAdviceTest程序中看到的,這個切面類定義了4個增強處理,這4個增強處理分別指定了相同的切入點表達式,這種做法顯然不符合軟件設計的原則:我們將那個切入點表達式重復了4次,如果需要該這個切入點,那么就要修改4處。
Spring AOP只支持以Spring Bean的方法執(zhí)行組作為連接點,
例如:
@Pointcut(”execution(* transfer(。。))“)
private void anyOldTransfer(){}一旦采用上面的代碼片段定義了名為anyOldTrandser的切入點之后,程序就可以重復使用該切入點了,甚至可以在其他切面類、其他包的切面類里使用該切入點,不過這取決于該方法簽名前的訪問修飾符。
切入點指示符:
正如前面的execution就是一個切入點指示符,Spring AOP僅僅支持部分AspectJ切入點指示符,不僅如此Spring AOP只支持使用方法調(diào)用作為連接點,所以Spring AOP的切入點指示符僅匹配方法執(zhí)行的連接點。
Spring AOP一共支持如下幾種切入點指示符:
----execution:用于匹配執(zhí)行方法的連接點,是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:指定方法聲明中的形參列表,支持兩個通配符:“*”、“。。”,其中*表示一個任意類型的參數(shù),而“。。”表示零個或多個任意類型的參數(shù)。
throws-pattern:指定方法聲明拋出的異常,支持通配符,該部分可以省略。
例如下面幾個execution表達式:
//匹配任意public方法的執(zhí)行。
execution(public * * (。。))
//匹配任意方法名以set開始的方法只想能夠。
execution(* set* (。。))
//匹配AccountService里定義的任意方法的執(zhí)行。
execution(* org.huaxia.AccountService.* (。。))
//匹配Service包中任意類的任意方法的執(zhí)行。
execution(* org.huaxia.service.*.*(。。))
----within:限定匹配特定類型的連接點,當使用Spring AOP的時候,只能匹配方法執(zhí)行的連接點,
例如下面幾個within表達式:
//在Service包中的任意連接點。
within(* org,huaxia.service.*)
//在Service包或者子包的任意連接點
within(* org.huaxia.service..*)
----this:用于限定AOP代理必須指定類型的實例,用于匹配該對象的所有連接點。當使用Spring AOP的時候,只能匹配方法執(zhí)行的連接點。
例如:
//匹配實現(xiàn)了AccountService接口的代理對象的所有連接點
this(org.huaxia.service.AccountService)
----target:用于限定目標對象必須是指定類型的實例,用于匹配該對象的所有連接點,當使用Spring AOP只匹配執(zhí)行方法的連接點。例如“”
//匹配實現(xiàn)了AccountService接口的目標對象的所有連接點
target(org.huaxia.service.AccountService)
----args:用于對連接點的參數(shù)類型進行限制,要求參數(shù)類型是指定類型的實例,例如:
//匹配只接受一個參數(shù),且傳入的參數(shù)類型是Serializable的所有連接點
args(java.io.Serializable)
注意:該例中給出的切入點表達式與execution(* *(java.io.Serializable))不同:args版本只匹配動態(tài)運行時傳入?yún)?shù)值是Serializable類型的情況,而execution版本只匹配方法簽名只包含一個Serializable類型的參數(shù)的方法。
----bean:用于指定只匹配指定Bean實例內(nèi)連接點,實際上只能使用方法執(zhí)行作為連接點,定義bean表達式需要傳入id或name,表示只匹配Bean實例內(nèi)連接點,支持“*”通配符。
例如:
//匹配tradeService Bean實例內(nèi)方法執(zhí)行的連接點
bean(tradeService)
//匹配名字以Service結(jié)尾的Bean實例內(nèi)方法執(zhí)行的連接點。
bean(*Service)
組合切入點表達式:
Spring支持3中邏輯運算符來組合切入點表達式:
&&:要求連接點同時匹配兩個切入點表達式;
||:只要連接點匹配任意一個切入點表達式;
!:要求連接點不匹配指定切入點表達式。
⑤AOP 基于配置Xml文件的管理方式
除了前面介紹的基于JDK1.5的Annotation方式來定義切面、切入點和增強處理,Spring AOP也允許使用XML文件來定義管理它們。
實際上,使用XML定義AOP也是@AspectJ一樣的同樣需要指定相關數(shù)據(jù):配置切面、切入點、增強處理所需要的信息完全一樣,只是提供這些信息的位置不一樣而已。使用XMLA文件配置AOPd的方式有很多優(yōu)點但是也有一些缺點:
xml配置方式比@AspectJ方式有更多的限制:僅支持“singleton”切面的Bean,不能在xml中組合多個命名連接點的聲明。
在Spring的配置文件中,所有的切面、切入點和增強處理都必須定義在《aop:config.。/》元素內(nèi)部。《beans.。/》元素下可以包含多個《aop:config.。/》元素。
一個《aop:config.。/》可以包含多個pointcut、advisor和aspect元素,且這3個元素必須按照此順序類定義。
注意:當我們使用《aop:config.。/》方式進行配置時,可能與Spring的自動代理方式?jīng)_突,例如我們使用BeanNameAutoProxyCreator或類似的方式顯示啟用了自動代理,則它可能導致問題(例如有些增請?zhí)幚頉]有被織入)因此要么全部使用自動代理的方式,要么全部使用《aop:config.。/》配置方式。不要不兩者混合使用。
————配置切面:
定義切面使用《aop:aspect.。/》元素,使用該元素來定義切面時,其實質(zhì)是將一個已有的Spring Bean轉(zhuǎn)換成切面Bean。
因為切面Bean可以當成一個普通的SpringBean來配置,所以可以為該切面Bean配置依賴注入。
配置《aop:aspect.。/》元素時可以指定如下3個屬性:
id:定義該切面的標識名;
ref:指定以指定ref所引用的的普通Bean作為切面Bean。
order:指定該切面Bean的優(yōu)先等級,數(shù)字越小,等級越大。
————配置增強處理:
《aop:before.。。/》:Before增強處理
《aop:after.。/》:After增強處理
《aop:after-returning.。。/》:afterReturning增強處理
《aop:after-throwing.。/》:afterThrowing增強處理
《aop:around.。。/》:Around增強處理
上面的元素不能配置子元素,但可以配置如下屬性:
pointcut:該屬性指定一個切入點表達式,Spring將在匹配該表達式的連接點時織入增強處理。
pointcut-ref:該屬性指定一個已經(jīng)存在的切入點的 名稱,通常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》
//業(yè)務類:
publicclass Chineseimplements Person {
public String sayHello(String word) {
System.out.println(”sayHello方法開始執(zhí)行。。.“);
return word;
}
publicvoid eat(String food) {
System.out.println(”我正在吃:“ + food);
}
publicvoid divide() {
int a = 5 / 0;
System.out.println(”divide執(zhí)行完畢/“ + a);
}
}
//切面Bean:
publicclass FourAdviceTest {
public Object proceedTX(ProceedingJoinPoint pre)throws Throwable {
System.out.println(”Around增強處理:執(zhí)行目標方法前,執(zhí)行模擬開啟事務。。.。。.。“);
Object[] objs = pre.getArgs();
if (objs !=null && objs.length 》 0
&& objs[0].getClass() == String.class) {
objs[0] =”被修改的參數(shù)“;
}
Object obj = pre.proceed(objs);
System.out.println(”Around增強處理:執(zhí)行目標方法之后,模擬結(jié)束事務。。.。。“);
return obj +”,新增加的內(nèi)容“;
}
publicvoid authority(JoinPoint jp) {
System.out.println(”Before增強:模擬權限檢查“);
System.out.println(”Before增強:被織入增強處理的目標方法:“
+ jp.getSignature().getName());
System.out.println(”Before增強:目標方法的參數(shù)為:“ + 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增強:目標方法的參數(shù)為:“
+ Arrays.toString(jp.getArgs()));
System.out.println(”AfterReturning增強:被注入增強的處理的目標對象:“ + jp.getTarget());
}
publicvoid release(JoinPoint jp) {
System.out.println(”After增強:模擬方法結(jié)束后,資源釋放。。.。。“);
System.out.println(”After增強:被織入增強處理的目標方法:“
+ jp.getSignature().getName());
System.out.println(”After增強:目標方法的參數(shù)為:“ + Arrays.toString(jp.getArgs()));
System.out.println(”After增強:被注入增強的處理的目標對象:“ + jp.getTarget());
}
}
輸出結(jié)果:
————配置切入點:
類似于@AspectJ方式,允許定義切入點來重用切入點表達式,XML配置方式也可以通過定義切入點來重用切入點表達式。
配置《aop:pointcut.。/》元素時通常需要指定如下兩個屬性:
id:指定該切入點的標識名;
expression:指定該切入點關聯(lián)的切入點表達式。
如下面代碼:
《aop:pointcut id=”myPointcut“ expression=”execution(* lee.*.*(。。))“/》
除此之外,如果程序已經(jīng)使用Annotation定義了切入點,在《aop:pointcut 。。/》元素中指定切入點表達式時還有另一種用法:
《aop:pointcut expression=”org.huaxia.SystemArchitecture.myPointcut()“》
5,Spring的事務:
Spring事務管理不需要與任何特定事務API耦合,對不同的持久化層訪問技術,編程式事務提供一致事務編程風格,通過模板化的操作一致性的管理事務。聲明式事務基于Spring AOP實現(xiàn),并不需要程序開發(fā)人員成為AOP專家,亦可以輕易使用Spring的聲明式事務管理。
Spring支持的事務策略:
JavaEE應用程序的傳統(tǒng)事務有兩種策略,全局事務和局部事務。全局事務由應用服務器管理,需要底層的服務器的JTA支持,局部事務和底層采用的持久化技術有關,當采用JDBC持久化技術時,需要采用Connection對象來操作事務;而采用Hibernate持久化技術時,需要使用session操作失誤。
當采用傳統(tǒng)的事務編程策略時,程序代碼必然和具體的事務操作代碼耦合。這樣的后果是:當應用程序需要在不同的的事務策略之間切換時,開發(fā)者必須手動修改程序代碼。當改為Spring操作事務策略時,即可改變這種狀況。
Spring事務策略是通過PlatformTransactionManager接口體現(xiàn)的,該接口是Spring事務策略的核心。

即使使用容器管理的JTA,代碼依然不需要JNDI查找,無需與特定的JTA資源耦合在一起,通過配置文件,JTA資源傳給PlatformTransactionManager的實現(xiàn)類,因此,程序的代碼可以在JTA事務管理和非JTA事務管理之間輕松的切換。
Spring是否支持事務跨多個數(shù)據(jù)庫資源?
答:Spring完全支持這種跨多個事務性資源的全局事務,前提是底層的應用服務器(如WebLogic、WebSphere等)支持JTA全局事務,可以這樣說:Spring本身沒有任何事務支持,它只是負責包裝底層的事務————當我們在程序中面向PlatformTransactionManager接口編程時,Spring在底層負責將這些操作轉(zhuǎn)換成具體的事務操作代碼,所以應用底層支持什么樣的事務策略,那么Spring就支持什么樣的事務策略。Spring事務管理的優(yōu)勢是將應用從具體的事務API中分離出來,而不是真正提供事務管理的底層實現(xiàn)。
Spring的具體的事務管理由PlatformTransactionManager的不同實現(xiàn)類來完成,在Spring容器中配置PlatformTransactionManager時必須針對不同的環(huán)境提供不同的實現(xiàn)類,下面提供了不同的持久化訪問環(huán)境,及其對應的PlatformTransactionManager實現(xiàn)類的配置。
1,BC數(shù)據(jù)源的局部事務策略的配置文件如下:
《!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫的驅(qū)動 --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的連接的最大空閑時間 --》
《property name=”maxIdleTime“ value=”20“/》
《/bean》
《!-- 配置JDBC數(shù)據(jù)源的局部事務管理器,使用DataSourceTransactionManager 類 --》
《!-- 該類實現(xiàn)PlatformTransactionManager接口,是針對采用數(shù)據(jù)源連接的特定實現(xiàn)--》
《bean id=”transactionManager“
class=”org.springframework.jdbc.datasource.DataSourceTransactionManager“》
《!-- 配置DataSourceTransactionManager時需要依注入DataSource的引用 --》
《property name=”dataSource“ ref=”dataSource“/》
《/bean》
,2,器管理JTA全局事務的配置文件如下:
《!-- 配置JNDI數(shù)據(jù)源Bean--》
《bean id=”dateSource“ class=”org.springframework.jndi.JndiObjectFactoryBean“》
《!--容器管理數(shù)據(jù)源的JNDI--》
《property name=”jndiName“ value=”jdbc/jpetstore“/》
《/bean》
《!--使用JtaTransactionManager類,該類實現(xiàn)PlatformTransactionManager接口--》
《!--針對采用全局事務管理的特定實現(xiàn)--》
《bean id=”transactionManager“ class=”org.springframework.transaction.jta.JtaTransactionManager“/》
3,采用Hibernate持久層訪問策略時,局部事務的策略的配置文件如下:
《!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫的驅(qū)動 --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的連接的最大空閑時間 --》
《property name=”maxIdleTime“ value=”20“/》
《/bean》
《!-- 定義Hibernate的SessionFactory --》
《bean id=”sessionFactory“
class=”org.springframework.orm.hibernate3.LocalSessionFactoryBean“》
《!-- 依賴注入數(shù)據(jù)源,注入正是上面定義的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》
《!-- 指定數(shù)據(jù)庫方言 --》
《prop key=”hibernate.dialect“》
org.hibernate.dialect.MySQLInnoDBDialect《/prop》
《!-- 是否根據(jù)需要每次自動創(chuàng)建數(shù)據(jù)庫 --》
《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的特定實現(xiàn)類--》
《bean id=”transactionManager“ class=”org.springframework.orm.hibernate3.HibernateTransactionManager“》
《property name=”sessionFactory“ ref=”sessionFactory“/》
《/bean》
4,底層采用Hibernate持久化技術,但事務依然采用JTA全局事務:
《!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫的驅(qū)動 --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的連接的最大空閑時間 --》
《property name=”maxIdleTime“ value=”20“/》
《/bean》
《!-- 定義Hibernate的SessionFactory --》
《bean id=”sessionFactory“
class=”org.springframework.orm.hibernate3.LocalSessionFactoryBean“》
《!-- 依賴注入數(shù)據(jù)源,注入正是上面定義的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》
《!-- 指定數(shù)據(jù)庫方言 --》
《prop key=”hibernate.dialect“》
org.hibernate.dialect.MySQLInnoDBDialect《/prop》
《!-- 是否根據(jù)需要每次自動創(chuàng)建數(shù)據(jù)庫 --》
《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類,該類實現(xiàn)PlatformTransactionManager接口--》
《!--針對采用全局事務管理的特定實現(xiàn)--》
《bean id=”transactionManager“ class=”org.springframework.transaction.jta.JtaTransactionManager“/》
從上面的配置文件可以看出,當使用Spring事務管理事務策略時,應用程序無需與具體的事務策略耦合,,Spring提供了兩種事務管理方式:
編程式管理方式:即使使用Spring編程式事務時,程序也可直接獲取容器中的TransactionManager Bean,該Bean總是PlatformTransactionManager的實例,可以通過該接口提供的3個方法來開始事務,提交事務和回滾事務。
聲明式管理方式:無需在Java程序中書寫任何的事務操作代碼,而是通過在XML文件中為業(yè)務組件配置事務代理(AOP代理的一種),AOP事務代理所織入的增強處理也是由Spring提供:在目標方法前,織入開始事務;在目標方法后執(zhí)行,織入結(jié)束事務。
注意:
Spring編程事務還可以通過TransactionTemplate類來完成,該類提供了一個execute方法,可以更簡潔的方式來進行事務操作。
當使用聲明式事務時,開發(fā)者無需書寫任何事務管理代碼,不依賴Spring或或任何事務API,Spring的聲明式事務編程無需任何額外的容器支持,Spring容器本身管理聲明式事務,使用聲明式事務策略,可以讓開發(fā)者更好的專注于業(yè)務邏輯的實現(xiàn)。
————使用TransactionProxyFactoryBean創(chuàng)建事務代理————
在Spring1.X,聲明事務使用TransactionProxyFactoryBean來配置事務代理Bean,正如他的名字所暗示的,它是一個工廠Bean,該工程Bean專為目標Bean生成事務代理Bean,既然TransactionProxyFactoryBean產(chǎn)生的是事務代理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》
《!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫的驅(qū)動 --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的連接的最大空閑時間 --》
《property name=”maxIdleTime“ value=”20“/》
《/bean》
《!-- 配置JDBC數(shù)據(jù)源的局部事務管理器,使用DataSourceTransactionManager 類 --》
《!-- 該類實現(xiàn)PlatformTransactionManager接口,是針對采用數(shù)據(jù)源連接的特定實現(xiàn)--》
《bean id=”transactionManager“
class=”org.springframework.jdbc.datasource.DataSourceTransactionManager“》
《!-- 配置DataSourceTransactionManager時需要依注入DataSource的引用 --》
《property name=”dataSource“ ref=”dataSource“/》
《/bean》
《!-- 配置一個業(yè)務邏輯Bean --》
《bean id=”test“ class=”lee.TestImpl“》
《property name=”ds“ ref=”dataSource“/》
《/bean》
《!-- 為業(yè)務邏輯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 + ”’)“);
//兩次插入相同的數(shù)據(jù),將違反主鍵約束
jt.execute(”insert into mytable values(‘“ + u + ”’)“);
//如果增加事務控制,我們發(fā)現(xiàn)第一條記錄也插不進去。
//如果沒有事務控制,則第一條記錄可以被插入
}
}
配置事務代理的時。需要傳入一個事務管理器,一個目標Bena,并指定該事務的事務屬性,事務實行由transactionAttribute指定,上面只有一條事務傳播規(guī)則,該規(guī)則指定對于所有方法都使用PROPAGATION_REQUIRED的傳播屬性,Spring支持的事務傳播規(guī)則:
PROPAGATION_MANDATORY:要求調(diào)用該方法的線程已處于事務環(huán)境中,否則拋出異常;
PROPAGATION_NESTED:如果執(zhí)行該方法的線程已處于事務的環(huán)境下,依然啟動新的事務,方法在嵌套的事務里執(zhí)行,如果執(zhí)行該方法的線程并未處于事務的環(huán)境下,也啟動新的事務,此時與PROPAGATION_REQUIRED;
PROPAGATION_NOT_SUPPORTED:如果調(diào)用該方法的線程處于事務中,則暫停事務,再執(zhí)行方法。
PROPAGATION_NEVER:不允許執(zhí)行該方法的線程處于事務的環(huán)境下,如果執(zhí)行該方法的線程處于事務的環(huán)境下,則會拋出異常;
PROPAGATION_REQUIRED:要求在事務環(huán)境中執(zhí)行該方法,如果當前執(zhí)行線程已處于事務中,則直接調(diào)用;否則,則啟動新的事務后執(zhí)行該方法。
PROPAGATION_REQUIREDS_NEW:該方法要求在新的事務中執(zhí)行,如果當前執(zhí)行線程已處于事務環(huán)境下,則暫停當前事務,啟動新事務后執(zhí)行方法;否則,啟動新的事務后執(zhí)行方法。
PROPAGATION_SUPPORTS:如果當前線程處于事務中,則使用當前事務,否則不使用事務。
主程序代碼:
public class MainTest
{
public static void main(String[] args)
{
//創(chuàng)建Spring容器
ApplicationContext ctx = new
ClassPathXmlApplicationContext(”bean.xml“);
//獲取事務代理Bean
Test t = (Test)ctx.getBean(”testTrans“);----------①
//執(zhí)行插入操作
t.insert(”bbb“);
}
}
上面①處代碼獲取testTrans Bean,該Bean已不再是TestImpl的實例了,它只是Spring容器創(chuàng)建的事務代理,該事務代理以TestImpl實例為目標對象,且該目標對象也實現(xiàn)了Test接口,故代理對象也可當作Test實例使用。
實際上,Spring不僅支持對接口的代理,整合GBLIB后,Spring甚至可以對具體類生成代理,只要設置proxyTargetClass屬性為true就可以了,如果目標Bean沒有實現(xiàn)任何接口,proxyTargetClass默認為true,此時Spring會對具體的類生成代理。當然通常建議面向接口編程,而不面向具體的實現(xiàn)類編程。
————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“》
《!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫的驅(qū)動 --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的連接的最大空閑時間 --》
《property name=”maxIdleTime“ value=”20“/》
《/bean》
《!-- 配置JDBC數(shù)據(jù)源的局部事務管理器,使用DataSourceTransactionManager 類 --》
《!-- 該類實現(xiàn)PlatformTransactionManager接口,是針對采用數(shù)據(jù)源連接的特定實現(xiàn)--》
《bean id=”transactionManager“
class=”org.springframework.jdbc.datasource.DataSourceTransactionManager“》
《!-- 配置DataSourceTransactionManager時需要依注入DataSource的引用 --》
《property name=”dataSource“ ref=”dataSource“/》
《/bean》
《!-- 配置一個業(yè)務邏輯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結(jié)尾的類
執(zhí)行的所有方法 --》
《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)
{
//創(chuàng)建Spring容器
ApplicationContext ctx = new
ClassPathXmlApplicationContext(”bean.xml“);
//獲取事務代理Bean
Test t = (Test)ctx.getBean(”test“);
//執(zhí)行插入操作
t.insert(”bbb“);
}
}
上面的配置文件直接獲取容器中的test Bean,因為Spring AOP會為該Bean自動織入事務增強處理的方式,所以test Bean的所有方法都具有事務性。
配置《tx:advice.。/》元素時除了需要一個transaction-Manager之外,重要的就是需要配置一個《attributes.。。/》子元素,該元素又可以包含多個《method.。/》子元素。
可以看出配置《tx:advice.。/》元素重點就是配置《method.。。/》子元素,每個《method.。/》子元素為一批方法指定所需的事務語義。
配置《method.。/》元素時可以指定如下屬性:
name:必選屬性,與該事務語義關聯(lián)的方法名,該屬性支持通配符。
propagation:指定事務傳播行為,該屬性可以為Propagation枚舉類的任意一個枚舉值,該屬性 的默認值是Propagation.REQUIRED;
isolation:指定事務隔離級別,該屬性值可為Isolation枚舉類的任意枚舉值。
timeout:指定事務超時時間(以秒為單位),指定-1意味不超時,默認值是-1;
read-only:指定事務是否只讀,該屬性默認為false;
rollback-for:指定觸發(fā)事務回滾的異常類(全名),該屬性可以指定多個異常類,用英文逗號隔開;
no-rollback-for:指定不觸發(fā)事務回滾的類,該屬性可以指定多個異常類,并且用英文的逗號隔開。
因此我們可以為不同的業(yè)務邏輯方法指定不同的事務策略,如:
《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框架擴張點,創(chuàng)建Spring容器。
第一種創(chuàng)建Spring容器的方式更加常見,為了讓Spring容器隨web應用啟動而啟動,有如下兩種方式:
利用ServletContextListener實現(xiàn);
采用load-on-startup Servlet實現(xiàn)。
Spring提供ServletContextListener的一個實現(xiàn)類ContextLoadListener,該類可以作為Listener使用,它會在創(chuàng)建時自動查找WEB-INF目錄下的ApplicationContext.xml,
如果只有一個配置文件,并且名字為ApplicationContext.xml,則只需要在web.xml文件添加如下代碼:
《listener》
《listener-class》org.springframework.web.context.ContextLoadListener《/listener-class》
《/listener》
如果需要加載多個配置文件,則考慮使用《context-param.。/》元素來確定配置文件的文件名。ContextLoadListener加載時,會查找名為contextConfigLocation的初始化參數(shù):
《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用于提供后臺服務的,主要用于創(chuàng)建Spring容器的,無需響應客戶端請求,因此無需為它配《servlet-mapping.。/》,如果有多個配置文件,或者文件名不是applicationContext,則 一樣使用《context-param.。。/》元素來確定多個配置文件。
ContextLoadListener和ContextLoadServlet底層都是依賴于ContextLoad。因此二者效果沒有什么區(qū)別,他們之間的區(qū)別不是它們本身 引起的,而是Servlet規(guī)范:Listener比Servlet優(yōu)先加載,因此采用ContextLoadListener創(chuàng)建ApplicationContext的時機更早。
②MVC框架與Spring整合的思考:
對于一個基于B/S架構的JavaEE而言,用戶請求總是向MVC框架的控制器請求,而當控制器攔截到用戶請求后,必須調(diào)用業(yè)務組件來處理用戶的請求,那么控制器如何獲取業(yè)務組件呢?
最容易想到的是,直接通過new 關鍵字創(chuàng)建業(yè)務邏輯,然后調(diào)用該業(yè)務邏輯組件的方法。
在實際應用中很少采用上面的訪問策略,至少有一下幾個原因:
1:控制器直接創(chuàng)建業(yè)務組件,導致控制器和業(yè)務組件的耦合降低到代碼層次,不利于高層次的解耦;
2:控制器不應該負責業(yè)務邏輯組件的創(chuàng)建,控制器只是業(yè)務邏輯的使用者,,無需關系業(yè)務邏輯組件的實現(xiàn);
3:每次創(chuàng)建新的業(yè)務組件導致性能降低。
答案是采用工廠模式或者服務定位器模式,對于采用服務定位模式,是遠程訪問的場景,在這種情形下,業(yè)務組件已經(jīng)在某個容器中運行,并對外提供某種服務,控制器無需理會該業(yè)務組件的創(chuàng)建,直接調(diào)用該服務即可,但在調(diào)用該服務前,必須先找到該服務。這就是服務定位模式的概念,經(jīng)典JavaEE應用就是這種結(jié)構的應用。
對于輕量級的JavaEE 應用,工廠模式則是更實際的策略,因為在輕量級JavaEE應用中,業(yè)務組件不是EJB,通常就是一個POJO,業(yè)務組件的生成通常應由工廠負責,而且工廠可以保證組件的實例只需要一個就夠了,可以避免重復實例化造成的系統(tǒng)開銷。
如果系統(tǒng)采用Spring框架,則Spring成為最大的工廠,那么控制器如何訪問到Spring容器中的業(yè)務組件呢?有兩種策略:
1,Spring容器負責管理控制器Action,并利用依賴注入為容器注入業(yè)務組件;
2,利用Spring的自動裝配,Action將會自動從Spring容器中獲取所需的業(yè)務邏輯組件。如:
Action類:
publicclass LoginActionimplements Action {
private Stringusername;
private Stringpassword;
private Stringtip;
private MyServicems;
publicString execute()throws Exception {
if(ms.valid(username,password)){
setTip(”哈哈哈。整合成功!“);
returnSUCCESS;
}
}
returnERROR;
}
業(yè)務組件:
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模式的核心思想是所有的數(shù)據(jù)庫訪問,都通過DAO組件完成,DAO組件封裝了數(shù)據(jù)庫的增刪改查等原子操作,業(yè)務邏輯組件依賴DAO組件提供的原子操作,
對于javaEE的應用架構,有非常多的,不管細節(jié)如何變化,javaEE大致 可以分為如下三層:
表現(xiàn)層;
業(yè)務邏輯層;
數(shù)據(jù)持久層;
Spring提供了一系列的抽象類,這些抽象類將被作為應用中DAO組件可以通過這些抽象類,Spring簡化了DAO的開發(fā)步驟,能夠以一致的訪問方式使用數(shù)據(jù)庫訪問技術,不管底層采用JDBC、JDO還是個Hibernate,應用中都采用一致的編程模型。
Spring提供的多種數(shù)據(jù)庫訪問技術的DAO支持,包括Hibernate、JDO、TopLink、iBatis、OJB、JPA等,就Hibernate的持久層訪問技術而言,Spring提供如下3個工具類來支持DAO組件的實現(xiàn):HibernateDaoSupport、HibernateTemplate、HibernateCallback。
2,管理Hibernate的SessionFactory
前面介紹的Hibernate的時候知道:通過Hibernate進行持久層訪問的時候,Hibernate的SessionFactory是非常重要的對象,它是單個數(shù)據(jù)庫映射關系編譯后的內(nèi)存鏡像,大部分情況下,一個java EE應用對應一個數(shù)據(jù)庫,即對應一個SessionFactory對象。
配置SessionFactory的示范代碼:
《!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫的驅(qū)動 --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫連接池的連接的最大空閑時間 --》
《property name=”maxIdleTime“ value=”20“/》
《/bean》
《!-- 定義Hibernate的SessionFactory --》
《bean id=”sessionFactory“
class=”org.springframework.orm.hibernate3.LocalSessionFactoryBean“》
《!-- 依賴注入數(shù)據(jù)源,注入正是上面定義的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》
《!-- 指定數(shù)據(jù)庫方言 --》
《prop key=”hibernate.dialect“》
org.hibernate.dialect.MySQLInnoDBDialect《/prop》
《!-- 是否根據(jù)需要每次自動創(chuàng)建數(shù)據(jù)庫 --》
《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可以通過構造方法參數(shù)傳入,也可以通過設值方式傳入。
示例:
public class PersonDaoImpl implements PersonDao
{
//定義一個HibernateTemplate對象,用于執(zhí)行持久化操作
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);
}
/**
* 根據(jù)用戶名查找Person
* @param name 查詢的人名
* @return 指定用戶名對應的全部Person
*/
public List《Person》 findByName(String name)
{
return (List《Person》)getHibernateTemplate()
.find(”from Person p where p.name like ?“ , name);
}
/**
* 查詢?nèi)縋erson實例
* @return 全部Person實例
*/
public List findAllPerson()
{
return (List《Person》)getHibernateTemplate()
.find(”from Person“);
}
/**
* 查詢數(shù)據(jù)表中Person實例的總數(shù)
* @return 數(shù)據(jù)表中Person實例的總數(shù)
*/
public long getPersonNumber()
{
return (Long)getHibernateTemplate().find
(”select count(*) from Person as p“)
.get(0);
}
}
4,使用HibernateCallback:
HibernateTemplate還提供一種更加靈活的方式來操作數(shù)據(jù)庫,,通過這種方式可以完全使用Hibernate的操作方式,HibernateTemplate的靈活方式是通過如下兩個方法來完成的。
Object execute(HibernateCallback action);
List executeFind(HibernateCallback action);
這兩個方法都需要一個HibernateCallback實例,HibernateCallback是一個接口,該接口包含一個方法doInHibernate(org.hibernate Session session),該方法是有一個Session參數(shù),當我們在開發(fā)中提供HibernateCallback實現(xiàn)類時,必須實現(xiàn)接口里的doInHibernate方法,該方法體內(nèi)即可獲得 Hibernate Session的引用。一旦獲取到Session的引用我們就可以完全以Hibernate的方式進行數(shù)據(jù)庫的訪問。
示例:
public class YeekuHibernateDaoSupport extends HibernateDaoSupport
{
/**
* 使用hql語句進行分頁查詢
* @param hql 需要查詢的hql語句
* @param offset 第一條記錄索引
* @param pageSize 每頁需要顯示的記錄數(shù)
* @return 當前頁的所有記錄
*/
public List findByPage(final String hql,
final int offset, final int pageSize)
{
//通過一個HibernateCallback對象來執(zhí)行查詢
List list = getHibernateTemplate()
.executeFind(new HibernateCallback()
{
//實現(xiàn)HibernateCallback接口必須實現(xiàn)的方法
public Object doInHibernate(Session session)
throws HibernateException, SQLException
{
//執(zhí)行Hibernate分頁查詢
List result = session.createQuery(hql)
.setFirstResult(offset)
.setMaxResults(pageSize)
.list();
return result;
}
});
return list;
}
/**
* 使用hql語句進行分頁查詢
* @param hql 需要查詢的hql語句
* @param value 如果hql有一個參數(shù)需要傳入,value就是傳入hql語句的參數(shù)
* @param offset 第一條記錄索引
* @param pageSize 每頁需要顯示的記錄數(shù)
* @return 當前頁的所有記錄
*/
public List findByPage(final String hql , final Object value ,
final int offset, final int pageSize)
{
//通過一個HibernateCallback對象來執(zhí)行查詢
List list = getHibernateTemplate()
.executeFind(new HibernateCallback()
{
//實現(xiàn)HibernateCallback接口必須實現(xiàn)的方法
public Object doInHibernate(Session session)
throws HibernateException, SQLException
{
//執(zhí)行Hibernate分頁查詢
List result = session.createQuery(hql)
//為hql語句傳入?yún)?shù)
.setParameter(0, value)
.setFirstResult(offset)
.setMaxResults(pageSize)
.list();
return result;
}
});
return list;
}
/**
* 使用hql語句進行分頁查詢
* @param hql 需要查詢的hql語句
* @param values 如果hql有多個個參數(shù)需要傳入,values就是傳入hql的參數(shù)數(shù)組
* @param offset 第一條記錄索引
* @param pageSize 每頁需要顯示的記錄數(shù)
* @return 當前頁的所有記錄
*/
public List findByPage(final String hql, final Object[] values,
final int offset, final int pageSize)
{
//通過一個HibernateCallback對象來執(zhí)行查詢
List list = getHibernateTemplate()
.executeFind(new HibernateCallback()
{
//實現(xiàn)HibernateCallback接口必須實現(xiàn)的方法
public Object doInHibernate(Session session)
throws HibernateException, SQLException
{
//執(zhí)行Hibernate分頁查詢
Query query = session.createQuery(hql);
//為hql語句傳入?yún)?shù)
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,實現(xiàn)DAO組件:
為了實現(xiàn)DAO組件,Spring提供了大量的XxxDaoSupport類,這些dao支持類已經(jīng)完成了大量基礎性的工作。
Spring為Hibernate的DAO提供工具類:HibernateDaoSupport,該類主要提供如下幾個方法來簡化DAO的實現(xiàn):
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);
}
/**
* 根據(jù)用戶名查找Person
* @param name 查詢的人名
* @return 指定用戶名對應的全部Person
*/
public List《Person》 findByName(String name)
{
return (List《Person》)getHibernateTemplate()
.find(”from Person p where p.name like ?“ , name);
}
/**
* 查詢?nèi)縋erson實例
* @return 全部Person實例
*/
public List findAllPerson()
{
return (List《Person》)getHibernateTemplate()
.find(”from Person“);
}
}
實際上,DAO的實現(xiàn)依然借助于HibernateTemplate的模板訪問方式,只是HibernateDaoSupport提供了兩個工具方法。
至此Java EE應用所需的各種組件都已經(jīng)出現(xiàn)了。
從用戶的角度上看,用戶發(fā)出HTTP請求,當MVC框架的控制器組件攔截到用戶的請求,將調(diào)用系統(tǒng)的業(yè)務邏輯組件,業(yè)務邏輯組件則調(diào)用 系統(tǒng)的DAO組件,,而DAO組件則依賴于SessionFactory和DataSOurce等底層的實現(xiàn)數(shù)據(jù)庫訪問。
從系統(tǒng)的實現(xiàn)角度上看,IoC容器先創(chuàng)建SessionFactory和DataSource等底層組件,然后將這些組件注入DAO組件,提供一個完整的Dao組件,并將DAO組件注入給業(yè)務邏輯組件,從而提供一個完整的業(yè)務邏輯組件,而業(yè)務邏輯組件又被注入給控制器組件,控制器組件負責攔截用戶請求,并將處理結(jié)果呈現(xiàn)給用戶。
電子發(fā)燒友App
























評論