Spring是一個(gè)開放源代碼的設(shè)計(jì)層面框架,他解決的是業(yè)務(wù)邏輯層和其他各層的松耦合問(wèn)題,因此它將面向接口的編程思想貫穿整個(gè)系統(tǒng)應(yīng)用。Spring是于2003 年興起的一個(gè)輕量級(jí)的Java 開發(fā)框架,由Rod Johnson創(chuàng)建。簡(jiǎn)單來(lái)說(shuō),Spring是一個(gè)分層的JavaSE/EEfull-stack(一站式) 輕量級(jí)開源框架。
spring特點(diǎn)
1.方便解耦,簡(jiǎn)化開發(fā)
通過(guò)Spring提供的IoC容器,我們可以將對(duì)象之間的依賴關(guān)系交由Spring進(jìn)行控制,避免硬編碼所造成的過(guò)度程序耦合。有了Spring,用戶不必再為單實(shí)例模式類、屬性文件解析等這些很底層的需求編寫代碼,可以更專注于上層的應(yīng)用。
2.AOP編程的支持
通過(guò)Spring提供的AOP功能,方便進(jìn)行面向切面的編程,許多不容易用傳統(tǒng)OOP實(shí)現(xiàn)的功能可以通過(guò)AOP輕松應(yīng)付。
3.聲明式事務(wù)的支持
在Spring中,我們可以從單調(diào)煩悶的事務(wù)管理代碼中解脫出來(lái),通過(guò)聲明式方式靈活地進(jìn)行事務(wù)的管理,提高開發(fā)效率和質(zhì)量。
4.方便程序的測(cè)試
可以用非容器依賴的編程方式進(jìn)行幾乎所有的測(cè)試工作,在Spring里,測(cè)試不再是昂貴的操作,而是隨手可做的事情。例如:Spring對(duì)Junit4支持,可以通過(guò)注解方便的測(cè)試Spring程序。
5.方便集成各種優(yōu)秀框架
Spring不排斥各種優(yōu)秀的開源框架,相反,Spring可以降低各種框架的使用難度,Spring提供了對(duì)各種優(yōu)秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
6.降低Java EE API的使用難度
Spring對(duì)很多難用的Java EE API(如JDBC,JavaMail,遠(yuǎn)程調(diào)用等)提供了一個(gè)薄薄的封裝層,通過(guò)Spring的簡(jiǎn)易封裝,這些Java EE API的使用難度大為降低。
7.Java 源碼是經(jīng)典學(xué)習(xí)范例
Spring的源碼設(shè)計(jì)精妙、結(jié)構(gòu)清晰、匠心獨(dú)運(yùn),處處體現(xiàn)著大師對(duì)Java設(shè)計(jì)模式靈活運(yùn)用以及對(duì)Java技術(shù)的高深造詣。Spring框架源碼無(wú)疑是Java技術(shù)的最佳實(shí)踐范例。如果想在短時(shí)間內(nèi)迅速提高自己的Java技術(shù)水平和應(yīng)用開發(fā)水平,學(xué)習(xí)和研究Spring源碼將會(huì)使你收到意想不到的效果。
spring原理機(jī)制
1,關(guān)于spring容器:
spring容器是Spring的核心,該 容器負(fù)責(zé)管理spring中的java組件,
ApplicationContext ctx = new ClassPathXmlApplicationContext(“bean.xml”);//這種方式實(shí)例化容器,容器會(huì)自動(dòng)預(yù)初始化所有Bean實(shí)例
ctx.getBean(“beanName”);
ApplicationContext 實(shí)例正是Spring容器。
ApplicationContext容器默認(rèn)會(huì)實(shí)例化所有的singleton Bean
Spring容器并不強(qiáng)制要求被管理組件是標(biāo)準(zhǔn)的javabean。
2,Spring的核心機(jī)制:依賴注入。
不管是依賴注入(Dependency Injection)還是控制反轉(zhuǎn)(Inversion of Conctrol),其含義完全相同:
當(dāng)某個(gè)java實(shí)例(調(diào)用者)需要調(diào)用另一個(gè)java實(shí)例(被調(diào)用者)時(shí),傳統(tǒng)情況下,通過(guò)調(diào)用者來(lái)創(chuàng)建被調(diào)用者的實(shí)例,通常通過(guò)new來(lái)創(chuàng)建,
而在依賴注入的模式下創(chuàng)建被調(diào)用者的工作不再由調(diào)用者來(lái)完成,因此稱之為“控制反轉(zhuǎn)”;創(chuàng)建被調(diào)用者實(shí)例的工作通常由Spring來(lái)完成,然后注入調(diào)用者,所以也稱之為“依賴注入”。
3,依賴注入一般有2中方式:
設(shè)置注入:IoC容器使用屬性的setter方式注入被依賴的實(shí)例?!秔roperty name=“” ref=“”》
構(gòu)造注入:IoC容器使用構(gòu)造器來(lái)注入被依賴的實(shí)例?!禼onstructor-arg ref=“”》
配置構(gòu)造注入的時(shí)候《constructor-arg》可以配置index屬性,用于指定該構(gòu)造參數(shù)值作為第幾個(gè)構(gòu)造參數(shù)值。下表從0開始。
4,Spring容器和被管理的bean:
Spring有兩個(gè)核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口。他們都可以代表Spring容器。
Spring容器是生成Bean實(shí)例的工廠,并管理Spring中的bean,bean是Spring中的基本單位,在基于Spring的java EE工程,所有的組件都被當(dāng)成bean處理。
包括數(shù)據(jù)源、Hibernate的SessionFactory、事務(wù)管理器。
?、賁pring容器:Spring最基本的接口就是BeanFactory,
BeanFactory有很多實(shí)現(xiàn)類,通常使用XmlBeanFactory,但是對(duì)于大部分的javaEE應(yīng)用而言,推薦使用ApplictionContext,它是BeanFactory的子接口,
ApplictionContext的實(shí)現(xiàn)類為FileSystemXmlApplicationContext和ClassPathXmlApplicationContext
FileSystemXmlApplicationContext:基于文件系統(tǒng)的XML配置文件創(chuàng)建ApplicationContext;
ClassPathXmlApplicationContext:基于類加載路徑下的xml配置文件創(chuàng)建ApplicationContext。
②ApplicationContext的事件機(jī)制,
ApplicationContext事件機(jī)制是基于觀察者設(shè)計(jì)模式實(shí)現(xiàn)的。通過(guò)ApplicationEvent類和ApplicationListener接口,
其中ApplicationEvent:容器事件,必須由ApplicationContext發(fā)布;
ApplicationListener:監(jiān)聽器,可有容器內(nèi)的任何監(jiān)聽器Bean擔(dān)任。
③容器中bean的作用域:
singleton:?jiǎn)卫J?,在整個(gè)Spring IoC容器中,使用singleton定義的bean將只有一個(gè)實(shí)例;
prototype:原型模式,每次通過(guò)容器的getBean方法獲取prototype定義的Bean時(shí),都將產(chǎn)生一個(gè)新實(shí)例;
request:對(duì)于每次HTTP請(qǐng)求中,使用request定義的bean都將產(chǎn)生一個(gè)新實(shí)例,只有在web應(yīng)用程序使用Spring時(shí),該作用域才有效;
session:同理
global session:同理
注意:request和session作用域只在web應(yīng)用中才生效,并且必須在web應(yīng)用中增加額外的配置才會(huì)生效,為了讓request,session兩個(gè)作用域生效,必須將HTTP請(qǐng)求對(duì)象綁定到為該請(qǐng)求提供服務(wù)的線程上,這使得具有request和session作用域的Bean實(shí)例能夠在后面的調(diào)用鏈中被訪問(wèn)。
當(dāng)支持Servlet2.4及以上規(guī)范的web容器時(shí),我們可以在web應(yīng)用的web.xml增加如下Listener配置,該Listener負(fù)責(zé)為request作用域生效:
《listener》
《listener-class》org.springframework.web.context.request.RequestContextListener《/listener-class》
《/listener》
如果僅使用了支持Servlet2.4以前規(guī)范的web容器,則該容器不支持Listener規(guī)范,故無(wú)法使用這種配置,可以使用Filter配置方式,我們可以在web應(yīng)用的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”/》
這樣容器就會(huì)為每次HTTP請(qǐng)求生成一個(gè)lee.Person的實(shí)例當(dāng)該請(qǐng)求響應(yīng)結(jié)束時(shí),該實(shí)例也隨之消失。
如果Web應(yīng)用直接使用Spring MVC作為MVC框架,即使用SpringDispatchServlet或DispatchPortlet來(lái)攔截所有用戶請(qǐng)求,則無(wú)需這些額外的配置,因?yàn)镾pringDispatchServlet或DispatchPortlet已經(jīng)處理了所有和請(qǐng)求有關(guān)的狀態(tài)處理。
?、塬@取容器的引用:
通常情況下:
Bean無(wú)需訪問(wèn)Spring容器,而是通過(guò)Spring容器訪問(wèn)的,即使 需要手動(dòng)訪問(wèn)Spring容器,程序也已通過(guò)類似下面的代碼獲取Spring容器 的引用。
ApllicationContext cts = ClassPathApplalicationContext(“bean.xml”);
但在一些極端的情況下,可能Bean需要訪問(wèn)Spring容器。Spring提供另一種方法訪問(wèn)Spring容器:
實(shí)現(xiàn)BeanFactoryAware接口的Bean,擁有訪問(wèn)Spring容器的能力,實(shí)現(xiàn)BeanFactoryAware的Bean被容器實(shí)例化后,會(huì)擁有一個(gè)引用指向創(chuàng)建他的BeanFactory。BeanFactoryAware只有一個(gè)方法setBeanFactory(BeanFactory beanFactory)該參數(shù)指向創(chuàng)建他的BeanFactory。
缺點(diǎn):污染了代碼,使代碼與Spring接口耦合在一起,因此沒(méi)有特別的必要,建議不要直接訪問(wèn)容器。
5,Bean實(shí)例的創(chuàng)建方式及對(duì)應(yīng)配置:
創(chuàng)建Bean的方法:
?、僬{(diào)用構(gòu)造器創(chuàng)建Bean實(shí)例;
?、谡{(diào)用靜態(tài)工廠方法創(chuàng)建Bean;
③調(diào)用實(shí)例工廠創(chuàng)建Bean。
調(diào)用靜態(tài)工廠方法創(chuàng)建Bean:
class屬性是必須的,但此時(shí)的class并不是指定Bean實(shí)例的實(shí)現(xiàn)類而是靜態(tài)工廠類。采用靜態(tài)工廠類需要配置如下兩個(gè)屬性:
class靜態(tài)工廠類的名字;
factory-method工廠方法(必須是靜態(tài)的)。
如果靜態(tài)工廠的方法有參數(shù)通過(guò)《constructor-arg/》元素知道。
調(diào)用實(shí)例工廠方法創(chuàng)建Bean:
使用實(shí)例工廠Bean時(shí)class屬性無(wú)需指定,因Spring容器不會(huì)直接實(shí)例化該Bean,
創(chuàng)建Bean時(shí)需要如下屬性:
factory-bean:該屬性為工廠Bean的ID;
factory-method:該屬性是定實(shí)例工廠的工廠方法。
6,入理解Spring容器中的Bean:
抽象Bean:
所有的抽象Bean,就是是定abstract屬性為true的Bean,抽象Bean不能被實(shí)例化,抽象Bean的價(jià)值在于被繼承
使用子Bean:
隨著應(yīng)用規(guī)模的增大,Spring配置文件的增長(zhǎng)速度更快。當(dāng)應(yīng)用中的組件越來(lái)越多,,Spring中的Bean配置也隨之大幅度增加。
就會(huì)出現(xiàn)一中現(xiàn)象:有一批配置Bean的信息完全相同,只有少量 的配置不同。怎么解決呢?
這時(shí)候就可以用Bean的繼承來(lái)解決。
注意:子Bean無(wú)法從父Bean繼承如下屬性:
depends-on,aotuwirwe,dependency-check,singleton,scope,lazy-iniyt這些屬性總是子Bean定義,或采用默認(rèn)值。
通過(guò) 為一個(gè)《bean.。。/》 元素指定parent屬性,即可指定該Bean是一個(gè)子Bean。
Bean繼承與java中繼承的區(qū)別:
Spring中的子bean和父Bean可以是不同類型,但java中的繼承則可保證子類是一種特殊的父類;
Spring中的Bean的繼承是實(shí)例之間的關(guān)系,因此只要表現(xiàn)在參數(shù)值的延續(xù),而java中的繼承是類之間的關(guān)系,主要表現(xiàn)為方法、屬性之間的延續(xù);
Spring中的子Bean不可以作為父Bean使用,不具備多態(tài)性,java中的子類完全可以當(dāng)成父類使用。
Bean的生命周期:
?、賡ingleton與prototype的區(qū)別:
singleton:Spring可以精確的知道該Bean何時(shí)被創(chuàng)建、初始化、銷毀。對(duì)于singleton作用域的Bean,每次客戶端請(qǐng)求Spring容器總會(huì)返回一個(gè)共享的實(shí)例。
prototype:Spring容器僅僅負(fù)責(zé)創(chuàng)建Bean,當(dāng)容器創(chuàng)建了Bean的實(shí)例后,Bean實(shí)例完全交給客戶端代碼管理,容器不在跟蹤其生命周期。
每次客戶端請(qǐng)求prototype作用域的Bean,都會(huì)為他創(chuàng)建一個(gè)新的實(shí)例,
?、谝蕾囮P(guān)系注入后的行為:
Spring提供兩種方法在Bean全部屬性設(shè)置成功后執(zhí)行特定的行為:
使用init-method屬性;
該Bean實(shí)現(xiàn)InitializingBean接口
第一種方法:使用init-method屬性指定某個(gè)方法在Bean全部屬性依賴關(guān)系設(shè)置結(jié)束后自動(dòng)執(zhí)行。使用這種方法不需要將代碼與Spring的接口耦合在一起,代碼污染少;
第二種方法:實(shí)現(xiàn)Initializing接口,該接口有一個(gè)方法void afterPropertiesSet() throws Exception,雖然實(shí)現(xiàn)次接口一樣可以在Bean全部屬性設(shè)置成功后執(zhí)行特定的行為,但是污染了代碼,是侵入式設(shè)計(jì),因此不推薦使用。
注意:如果即采用init-method屬性指定初始化方法,又實(shí)現(xiàn)InitializingBean接口來(lái)指定初始化方法,先執(zhí)行initializingBean接口中定義的方法,再執(zhí)行init-method屬性指定的方法。
③Bean銷毀之前行為:
與定制初始化相似,Spring也提供兩種方式定制Bean實(shí)例銷毀之前的特定行為,如下:
使用destroy-method屬性:
實(shí)現(xiàn)DisposableBean接口:
注意:如果即采用destroy-method屬性指定銷毀之前的方法,又實(shí)現(xiàn)DisposableBean接口來(lái)指定指定銷毀之前的方法,與②類似。
④default-init-method與default-destroy-method屬性,指定了所有的Bean都會(huì)執(zhí)行此方法,而不是單個(gè)的Bean。
協(xié)調(diào)作用域不同步的Bean:
描述:
當(dāng)Spring容器中作用域不同的Bean相互依賴時(shí),可能出現(xiàn)一些問(wèn)題:
當(dāng)兩個(gè)singleton作用域Bean存在依賴關(guān)系時(shí),或當(dāng)prototype作用依賴singleton作用域的Bean時(shí),通過(guò)屬性定義依賴關(guān)系即可。、
但是,當(dāng)singleton作用域的Bean依賴prototype作用域Bean時(shí),singleton作用域的Bean只有一次初始化的機(jī)會(huì),他的依賴關(guān)系也只有在初始化階段被設(shè)置,而他所依賴的prototype作用域的Bean則會(huì)不斷的產(chǎn)生新的Bean實(shí)例。
解決方案:
第一種:部分放棄依賴注入:singleton作用域的Bean每次需要prototype作用域的Bean,則主動(dòng)向容器請(qǐng)求新的Bean實(shí)例。
第二種:利用方法注入。
第一種方案肯定是不好的,代碼主動(dòng)請(qǐng)求新的Bean實(shí)例,必然會(huì)導(dǎo)致與Spring API耦合,造成代碼嚴(yán)重污染。
通常情況下采用第二中方式。
方法注入通常使用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;
//定義一個(gè)抽象方法,該方法將由Spring負(fù)責(zé)實(shí)現(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與前面介紹的實(shí)例工廠方法創(chuàng)建Bean、靜態(tài)工廠創(chuàng)建Bean有所區(qū)別:
前面的那些工廠是標(biāo)準(zhǔn)的工廠模式,Spring只是負(fù)責(zé)調(diào)用工廠方法來(lái)創(chuàng)建Bean實(shí)例;
此處工廠Bean是Spring的一種特殊Bean,這種工廠Bean必須實(shí)現(xiàn)FactoryBean接口。
FactoryBean接口是工廠Bean標(biāo)準(zhǔn)的工廠Bean的接口,實(shí)現(xiàn)該接口的Bean只能當(dāng)工廠Bean使用,當(dāng)我們將工廠Bean部署在容器中,并通過(guò)getBean()方法來(lái)獲取工廠Bean,容器不會(huì)返回FactoryBean實(shí)例而是FactoryBean的產(chǎn)品。
FactoryBean提供了三個(gè)方法:
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;
}
}
《!--配置一個(gè)FactoryBean,和普通的Bean一樣--》
《bean id=“chinese” class=“”/》
public static void main(String args[]){
//以classpth下的bean.xml創(chuàng)建Reource對(duì)象
ClassPathResource re = new ClasspathResource(“bean.xml”);
//創(chuàng)建BeanFactory
XmlBeanFactory factory = new XmlBeanFactory(re);
Person p = (Person)factory.getBean(“chinese”);
//如需要獲取FactoryBean本身則應(yīng)該在bean id前加&
Person p = (Person)factory.getBean(“&chinese”);
}
對(duì)于初學(xué)者可能無(wú)法體會(huì)到工廠bean的作用,實(shí)際上,F(xiàn)actoryBean是Spring中非常有用的接口。例如:TransationProxyFactroyBean,這個(gè)工廠轉(zhuǎn)為目標(biāo)Bean創(chuàng)建事務(wù)代理。
7,深入理解依賴關(guān)系配置
組件與組件之間的耦合,采用依賴注入管理,但是普通的javabean屬性值,應(yīng)直接在代碼里設(shè)置。
對(duì)于singleton作用域的bean,如果沒(méi)有強(qiáng)制取消其預(yù)初始化行為,系統(tǒng)會(huì)在創(chuàng)建Spring容器時(shí)預(yù)初始化所有的singleton作用域的bean,與此同時(shí),該bean依賴的bean也一起被實(shí)例化。
BeanFactory與ApplicationContext實(shí)例化容器中的bean的時(shí)機(jī)不同,前者等到程序需要Bean實(shí)例才創(chuàng)建Bean
后者會(huì)預(yù)初始化容器中的所有Bean。
因?yàn)椴捎肁pplicationContext作為Spring的容器,創(chuàng)建容器時(shí),會(huì)創(chuàng)建容器中所有singleton作用域的所有bean,因此可能需要更多的系統(tǒng)資源,但是一旦創(chuàng)建成功。應(yīng)用后面的 響應(yīng)速度會(huì)很快,因此,對(duì)于普通的javaEE而言 ,建議使用ApplicationContext作為Spring的容器。
Bean實(shí)例4中屬性值的設(shè)置:
value;ref;bean;list、set、map、props
?、僭O(shè)置普通屬性值value,略;
?、谂渲煤献髡連ean ref
可以為ref元素指定兩個(gè)屬性:bena、Local
bean:引用在不同一份XML配置文件中的其他Bean實(shí)例的ID屬性值;
Local:引用同一份XML配置文件的其他Beanid屬性值。
也可以不配置以上兩個(gè)屬性。
?、劢M合屬性名稱:
public class A{
private Person p = new Person();
set/get.。。.
}
Spring配置文件
《bean id=“a” class=“A”》
《property name=“p.name” value=“aaa”/》
《/bean》
?、茏⑷肭短譈ean:
《bean id=“” class=“”》
《 property name=“”》
//屬性為嵌套Bean 不能由Spring容器直接訪問(wèn),因此沒(méi)有id屬性
《bean class=“。。.”/》
《/property》
《/bean》
?、葑⑷爰现担?/p>
《list》
《value》《/value》
《value》《/value》
《/list》
《map》
//每一個(gè)entry配置一個(gè)key-value對(duì)
《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》
?、拮⑷敕椒ǚ祷刂担?/p>
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,強(qiáng)制初始化Bean:
Spring有一個(gè)默認(rèn)的規(guī)則,總是先初始化主調(diào)Bean,然后在初始化依賴Bean。
為了指定Bean在目標(biāo)Bean之前初始化,可以使用depends-on屬性
9,自動(dòng)裝配:
Spring能自動(dòng)裝配Bean與Bean之間的依賴關(guān)系,即使無(wú)需使用ref顯式指定依賴Bean。
Spring的自動(dòng)裝配使用autowire屬性指定,每一個(gè)《bean/》元素都可以指定autowire屬性,也就是說(shuō)在Spring容器中完全可以讓某些Bean自動(dòng)裝配,而某些Bean不沒(méi)使用自動(dòng)裝配。
自動(dòng)裝配可以減少配置文件的工作量,但是降低了依賴關(guān)系的透明性和依賴性。
使用autowire屬性自動(dòng)裝配,autowire屬性可以接受如下幾個(gè)值 :
no:不使用自動(dòng)裝配。這是默認(rèn)配置。
byName:根據(jù)屬性自動(dòng)裝配,BeanFactory會(huì)查找容器中所有的Bean,找出id屬性與屬性名同名的Bean來(lái)完成注入。如果沒(méi)有找到匹配的Bean,Spring則不會(huì)進(jìn)行任何注入。
byType:根據(jù)屬性類型自動(dòng)裝配,BeanFactroy會(huì)查找容器中所有的 Bean,如果一個(gè)正好與依賴屬性類型相同的Bean,就會(huì)自動(dòng)注入這個(gè)屬性。
如果有多個(gè)這樣的Bean則會(huì)拋出異常。如果沒(méi)有這樣 的Bean則什么也不會(huì)發(fā)生,屬性不會(huì)被設(shè)置。
constructor:與byType類似,區(qū)別是用于構(gòu)造注入的參數(shù),如果BeanFactory中不是恰好有一個(gè)Bean與構(gòu)造器參數(shù)類型相同。則會(huì)拋出異常。
autodetect:BeanFactory會(huì)根據(jù)Bean內(nèi)部的結(jié)構(gòu),決定使用constructor或byType,如果找到一個(gè)缺省的構(gòu)造器,就會(huì)應(yīng)用byType。
注意:對(duì)于大型的應(yīng)用而言,不鼓勵(lì)使用自動(dòng)裝配,
10,依賴檢查:
Spring提供一種依賴檢查的功能,可以防止出現(xiàn)配置手誤,或者其他情況的錯(cuò)誤。
使用依賴檢查可以讓系統(tǒng)判斷配置文件的依賴關(guān)系注入是否完全有效。
使用依賴檢查,可以保證Bean的屬性得到了正確的設(shè)置,有時(shí)候,某個(gè)Bean的特定屬性并不需要設(shè)置值,或者某些屬性已有默認(rèn)值,此時(shí)采用依賴檢查就會(huì)出現(xiàn)錯(cuò)誤,該Bean就不應(yīng)該采用依賴檢查,幸好Spring可以為不同的Bean單獨(dú)指定依賴檢查的行為,Spring提供dependency-chech屬性來(lái)配置依賴檢查,當(dāng)然也可以指定不同的檢查依賴策略。
該屬性有如下值:
none:不進(jìn)行依賴檢查,沒(méi)有指定值的Bean屬性僅僅是沒(méi)有設(shè)置值,這是默認(rèn)值。
simple:對(duì)基本類型和集合(除了合作者Bean)進(jìn)行依賴檢查。
objects:僅對(duì)合作者Bean進(jìn)行依賴檢查。
all:對(duì)合作者Bean、基本數(shù)據(jù)類型全部進(jìn)行依賴檢查。
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ū)?huì)拋出異常,雖然Chinese類的age屬性已經(jīng)有了默認(rèn)值,但配置dependency-check=”all“則要求配置文件為所有的屬性都提供正確的值。
而age屬性沒(méi)有提供值。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
二:深入Spring:
1,利用后處理器擴(kuò)展Spring容器:
Spring容器提供了很好的擴(kuò)展性,除了可以與各種第三方框架良好的整合外,其IoC容器也允許開發(fā)者進(jìn)行擴(kuò)展,這種擴(kuò)展甚至無(wú)需實(shí)現(xiàn)BeanFactory或ApplicationContext接口,允許兩個(gè)后處理器對(duì)IoC容器進(jìn)行擴(kuò)展:
Bean后處理器:這種處理器會(huì)對(duì)容器中的Bean進(jìn)行后處理,對(duì)Bean的功能進(jìn)行額外的加強(qiáng)。
容器后處理器:這種處理器對(duì)IoC容器進(jìn)行后處理,用于增強(qiáng)容器的功能。
?、貰ean后處理器:
Bean后處理器是一種特殊的Bean,這種特殊的Bean并不對(duì)外提供服務(wù),它甚至可以沒(méi)有id屬性。它主要負(fù)責(zé)對(duì)容器中的其他Bean執(zhí)行后處理。
后處理器必須實(shí)現(xiàn)BeanPostProcessor接口。此接口有兩個(gè)方法:Object postProcessorBeforeInitialization(Object object,String name) Object postProcessorAfterInitialization(Object object,String name)
第一個(gè)參數(shù)是系統(tǒng)即將后處理的Bean的實(shí)例,第二個(gè)參數(shù)是該Bean實(shí)例的名稱。
public class MyBeanPostProcessor implements BeanPostProcessor{
public Object postProcessorBeforeInitialization(Object object,String name) throws Beanexception{
System.out.println(”后處理器在初始化之前對(duì)“+”name“+”進(jìn)行增強(qiáng)處理“);
return object;
}
public Object postProcessorAfterInitialization(Object object,String name) throws Beanexception{
System.out.println(”后處理器在初始化之后對(duì)“+”name“+”進(jìn)行增強(qiáng)處理“);
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“)
//注冊(cè)BeanPostProcessor
factory.addPostProcessor(processor )
Person p = (Person)factory.getBean(”chinese“);
p.useAxe();
}
程序輸出name屬性時(shí),不會(huì)輸出李四,而是張三,雖然注入的值是李四,但是Bean后處理器發(fā)生作用。
輸出:
---------------------------------------------------------------
Spring執(zhí)行注入依賴關(guān)系。。.。。.
后處理器在初始化之前對(duì)chinese進(jìn)行增強(qiáng)處理
初始化工作init
后處理器在初始化之后對(duì)chinese進(jìn)行增強(qiáng)處理
張三
----------------------------------------------------------------
如果使用BeanFactory作為Spring的容器。則必須手動(dòng)注冊(cè)Bean后處理器,容器中一旦注冊(cè)了Bean后處理器,Bean后處理器就會(huì)自動(dòng)啟動(dòng),在容器中每個(gè)Bean創(chuàng)建時(shí)自動(dòng)工作。
實(shí)現(xiàn)BeanPostProcessor接口的Bean后處理器可以對(duì)Bean進(jìn)行任何操作,包括完全忽略這個(gè)回調(diào),BeanPostProcessor通常用來(lái)檢查標(biāo)記接口,或者做將Bean包裝成一個(gè)Proxy的事情。
如果不采用BeanFactory作為Spring的容器,采用ApplicationContext作為容器,則無(wú)需手動(dòng)注冊(cè)Bean后處理器,ApplicationContext可自動(dòng)檢測(cè)到容器中的Bean后處理器,自動(dòng)注冊(cè)。Bean后處理器會(huì)在創(chuàng)建Bean時(shí)自動(dòng)啟動(dòng)。對(duì)于后處理器而言,ApplicationContext作為Spring容器更為方便。
?、谌萜骱筇幚砥鳎?/p>
除了上面的提供的Bean后處理器外,Spring還提供了一種容器后處理器,,Bean后處理器負(fù)責(zé)容器中所有Bean實(shí)例的,而容器后處理器負(fù)責(zé)處理容器本身。
容器后處理器必須要實(shí)現(xiàn)BeanFactoryPostProcessor接口。
類似于Bean后處理器,采用ApplicationContext作為容器,則無(wú)需手動(dòng)注冊(cè)容器后處理器。如果使用BeanFactory作為Spring的容器。則必須手動(dòng)注冊(cè)容器后處理器。
2,Spring的“零配置”支持
?、偎阉鰾ean類
Spring提供如下幾個(gè)Annotation來(lái)標(biāo)注Spring Bean:
@Component標(biāo)注一個(gè)普通的Spring Bean;
@Controller:標(biāo)注一個(gè)控制器組件類;
@Service:標(biāo)注一個(gè)業(yè)務(wù)邏輯組件類;
@Repository:標(biāo)注一個(gè)Dao組件;
《context:component-scan base-package=”“/》
在默認(rèn)的情況下,自動(dòng)搜索所有以@Component、@Controller、@Service、@Repository注釋的java類,當(dāng)作Spring Bean處理。
還可以通過(guò)《context:component-scan base-package=”“/》子元素《include-filter》/《exclude-filter》
?、谥付˙ean的作用域:
如:
@Scope(”prototype“)
@Component(”axe“)
public class SteelAxe implements Axe{
}
③使用@Resource配置依賴:
@Resource有一個(gè)name屬性,在默認(rèn)的情況下,Spring將這個(gè)值 解釋為需要被注入的Bean的實(shí)例的名字,換句話說(shuō),使用@Resource與《property.。/》元素的ref屬性具有相同的效果。
當(dāng)使用@Resource修飾setter方法,如果省略name屬性,則name屬性默認(rèn)是從該setter方法去掉set子串,首字母小寫的到的子串。
當(dāng)使用@Resource修飾Field時(shí),如果mame,則默認(rèn)與Field的相同。
?、茏詣?dòng)裝配和精確裝配:
Spring提供@Autowired來(lái)指定自動(dòng)裝配,使用@Autowired可以標(biāo)志setter方法、普通方法、和構(gòu)造器。如:
@Component
public class Chinese implements Person{
@Autowired
private Aex axe;
@Autowired
public Chinese(Axe axe){
this.axe = axe
}
@Autowired
private Axe [] axes;
}
當(dāng)@Autowired標(biāo)注Field時(shí)Spring會(huì)把容器中的與該Field類型匹配的Bean注入該屬性,
如果Spring容器中有多個(gè)同類型的Bean與Field類型匹配,則會(huì)出現(xiàn)異常。
當(dāng)@Autowired標(biāo)注數(shù)組類時(shí),在這種情況,Spring會(huì)自動(dòng)搜索Spring容器中所有與數(shù)組類型相匹配的類型的Bean,并把這些Bean當(dāng)作數(shù)組的元素來(lái)創(chuàng)建數(shù)組。
當(dāng)@Autowired標(biāo)注集合時(shí),和標(biāo)注數(shù)組類似,當(dāng)時(shí)必須使用泛型。
?、菔褂聾Qualifier精確裝配
正如上面的@Autowired總是采用byType的自動(dòng)裝配策略。在這種情況下,符合自動(dòng)和裝配的類型的Bean常常有多個(gè)這個(gè)時(shí)候就會(huì)引發(fā)異常了。
為了實(shí)現(xiàn)精確的配置,Spring提供@Qualifier,可以根據(jù)Bean標(biāo)識(shí)來(lái)指定自動(dòng)裝配,
--- @Qualifier可以標(biāo)注Field,如:
@Component
public class Chinese implements Person{
@Autowired
@Qualifier(”steelAxe“)
private Aex axe;
}
--- @Qualifier還可以標(biāo)注方法的形參,如:
@Component
public class Chinese implements Person{
@Autowired
private Aex axe;
public void setAxe(@Qualifier(”steelAxe“) Axe axe )
this.axe=axe;
}
}
3,資源訪問(wèn):
4,Spring的AOP:
?、貯spectJ
?、贏OP的基本概念:
AOP框架不與特定的代碼耦合。下面是一些面向切面編程的術(shù)語(yǔ):
切面(Aspect):業(yè)務(wù)流程運(yùn)行到某個(gè)特定的步驟,也就是一個(gè)應(yīng)用運(yùn)行的關(guān)注點(diǎn),關(guān)注點(diǎn)可能橫切多個(gè)對(duì)象,所以常常也被稱為橫切關(guān)注點(diǎn)。
連接點(diǎn)(Joinpoint):程序執(zhí)行過(guò)程明確的點(diǎn),如方法的調(diào)用、異常的拋出,Spring AOP中連接點(diǎn)總是方法的調(diào)用。
增強(qiáng)處理(Advice):AOP框架在特定的切入點(diǎn)執(zhí)行的增強(qiáng)處理,處理有around、before和After等類型。
切入點(diǎn)(Pointcut):可以插入增強(qiáng)處理的連接點(diǎn),簡(jiǎn)而言之,當(dāng)某個(gè)連接點(diǎn)滿足指定要求時(shí),該連接點(diǎn)將被添加增強(qiáng)處理,該連接點(diǎn)也就變成了切入點(diǎn),
例如一下代碼:
pointcut xxxPointcut(){
?。篹xecution(void H*.say*());
}
每個(gè)方法的調(diào)用都只是連接點(diǎn),但如果該方法屬于H開頭的類,且方法名以say開頭,按照方法的執(zhí)行將變成切入點(diǎn)。
引入:將方法添加到被處理的類中,Spring允許引入新的接口到任何被處理的對(duì)象。例如:你可以使用一個(gè)引用,使任何對(duì)象實(shí)現(xiàn)IsModified接口,以此簡(jiǎn)化緩存。
目標(biāo)對(duì)象:被AOP增強(qiáng)處理的對(duì)象,也被稱為被增強(qiáng)的對(duì)象,如果AOP框架是通過(guò)運(yùn)行時(shí)代理來(lái)實(shí)現(xiàn)的,那么這個(gè)對(duì)象將是一個(gè)被代理的對(duì)象。
AOP代理:AOP框架創(chuàng)建的 對(duì)象,簡(jiǎn)單的說(shuō),代理就是對(duì)目標(biāo)對(duì)象的加強(qiáng),Spring的AOP代理可以使JDK動(dòng)態(tài)代理,也可以是CGLIB代理,前者為實(shí)現(xiàn)接口的目標(biāo)對(duì)象的代理后者為不實(shí)現(xiàn)接口的目標(biāo)對(duì)象的代理。
織入(Weaving):將增強(qiáng)處理增加到目標(biāo)對(duì)象中,并創(chuàng)建一個(gè)被增強(qiáng)的對(duì)象(AOP代理)的過(guò)程就是織入??椚胗袃煞N實(shí)現(xiàn)方式:編譯時(shí)增強(qiáng)(例如AspectJ)和運(yùn)行時(shí)增強(qiáng)(例如CGLIB)
目前Spring只支持將方法調(diào)用作為連接點(diǎn),如果需要把Field的訪問(wèn)和更新也作為增強(qiáng)處理的連接點(diǎn),可以使用AspectJ。
一旦我們掌握了上面的AOP的相關(guān)概念,不難發(fā)現(xiàn)進(jìn)行AOP的編程其實(shí)是一件很簡(jiǎn)單的事情,縱觀AOP編程需要程序員參與的只有3個(gè)部分:
定義普通業(yè)務(wù)組件;
定義切入點(diǎn),一個(gè)切入點(diǎn)橫切多個(gè)業(yè)務(wù)組件;
定義增強(qiáng)處理,增強(qiáng)處理就是現(xiàn)在AOP框架為普通業(yè)務(wù)組件織入的處理動(dòng)作。
③AOP,基于Annotation的零配置方式:
定義Before增強(qiáng)處理:
當(dāng)我們?cè)谝粋€(gè)切面類里使用@Before來(lái)標(biāo)注一個(gè)方法時(shí),該方法將作為Before增強(qiáng)處理,使用@Before標(biāo)注時(shí)通常要指定一個(gè)value屬性值,用來(lái)指定一個(gè)切入點(diǎn)表達(dá)式(既可以是一個(gè)已有的切入點(diǎn),也可以直接定義切入點(diǎn)表達(dá)式),用于指定該增強(qiáng)處理將被織入那些切入點(diǎn)。
注意:(”execution(* cn.huaxia.spring.*.*(。。))“)第一個(gè)星號(hào)后面一定要有一個(gè)空格。
//定義一個(gè)切面
@Aspect
publicclass BeforeAdviceTest {
// 執(zhí)行cn.huaxia.spring包下的所有方法都做為切入點(diǎn)
@Before(”execution(* cn.huaxia.spring.*.*(。。))“)
publicvoid authority() {
System.out.println(”模擬執(zhí)行權(quán)限檢查。。.“);
}
}
上面@Aspect@標(biāo)注BeforeAdviceTest 表明該類是一個(gè)切面類,在該切面里定義了一個(gè)authority()方法,這個(gè)方法本來(lái)沒(méi)有什么特別之處,但是因?yàn)槭褂聾Before來(lái)標(biāo)注該方法,這就將該方法轉(zhuǎn)換成一個(gè)增強(qiáng)處理。上面程序中使用@Before Annotation標(biāo)注時(shí),直接指定切入點(diǎn)表達(dá)式,指定cn.huaxia.spring下的所有方法都作為切入點(diǎn)。
@Component
publicclass Chineseimplements Person {
@Override
public String sayHello(String word) {
return word;
}
publicvoid eat(String food) {
System.out.println(”我正在吃“ + food);
}
}
從上面Chinese類的代碼來(lái)看,他是一個(gè)如此純凈的Java類,他絲毫不知將被誰(shuí)來(lái)進(jìn)行增強(qiáng)處理,也不知道將被怎樣增強(qiáng)處理---正式這種無(wú)知才是”AOP“最大的魅力:目標(biāo)類可以被無(wú)限增強(qiáng)。
在Spring配置文件中配置自動(dòng)搜索Bean組件,配置自動(dòng)搜索切面類,Spring AOP自動(dòng)對(duì)Bean組件進(jìn)行增強(qiáng)。配置文件如下:
《?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組件、自動(dòng)搜索切面類--》
《context:component-scanbase-package=”cn.huaxia.spring“》
《context:include-filtertype=”annotation“
expression=”org.aspectj.lang.annotation.Aspect“/》
《/context:component-scan》
《!-- 啟動(dòng)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“);//會(huì)出錯(cuò),不能轉(zhuǎn)換為Chinese
Person c = (Person)ac.getBean(”chinese“);
c.eat(”西瓜“);
System.out.println(c.sayHello(”你好--Before“));
}
}
定義AfterReturning增強(qiáng)處理:
AfterReturning增強(qiáng)處理在目標(biāo)方法正常完成之后織入。
類似于使用@Before Annotation可標(biāo)注Before增強(qiáng)處理,使用@AfterReturning Annation來(lái)標(biāo)注@AfterRetuning增強(qiáng)處理。
使用@AfterReturning Annotation可以指定如下屬性:
pointcut/value:這兩個(gè)屬性的作用是一樣的,他們都是指定該切入點(diǎn)對(duì)應(yīng)的切入表達(dá)式,當(dāng)指定pointcut屬性,value屬性值將會(huì)被覆蓋。
returning:指定一個(gè)返回值形參,增強(qiáng)處理的方法可以通過(guò)該形參名來(lái)訪問(wèn)目標(biāo)方法的返回值。
@Aspect
publicclass AfterReturningAdviceTest {
@AfterReturning(pointcut=”execution(* cn.huaxia.spring.*.*(。。))“,returning=”obj“)
publicvoid log(Object obj){
System.out.println(”獲取目標(biāo)方法返回值“+obj);
System.out.println(”模擬日志記錄功能。。.。“);
}
}
如果log有參數(shù),returning屬性一定要配置。如果參數(shù)不止一個(gè),那么目標(biāo)方法將不會(huì)執(zhí)行必須要配置args表達(dá)式。
注意:使用returning屬性還有另一個(gè)額外的作用:它可以限定切入點(diǎn)只匹配具有對(duì)應(yīng)返回類型的方法。加入上面的log的參數(shù)是String類型,則該切入點(diǎn)只匹配cn.huaxia.spring包下的返回值類型為String的所有方法。雖然AfterReturning增強(qiáng)處理可以訪問(wèn)到目標(biāo)方法的返回值,但不可以改變目標(biāo)方法返回值。
定義AfterThrowing增強(qiáng)處理:
AfterThrowing增強(qiáng)處理主要用于程序中未處理的異常。
使用@AfterThrowing Annotation時(shí)可以指定如下兩個(gè)屬性:
pointcut/value
throwing:指定一個(gè)返回值形參名,增強(qiáng)處理定義的方法可以通過(guò)該形參名來(lái)訪問(wèn)目標(biāo)方法中所拋出的異常對(duì)象。
@Aspect
publicclass AfterThrowingAdviceTest {
@AfterThrowing(pointcut =”execution(* cn.huaxia.spring.*.*(。。))“, throwing =”th“)
publicvoid doRecoveryActions(Throwable th) {
System.out.println(”目標(biāo)方法中拋出的異?!?+ th);
System.out.println(”模擬拋出異常的增強(qiáng)處理/。。.。?!埃?
}
}
@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“);//會(huì)出錯(cuò),不能轉(zhuǎn)換為Chinese
Person c = (Person)ac.getBean(”chinese“);
System.out.println(c.sayHello(”你好--Before“));
c.eat(”西瓜“);
c.divide();
}
輸出的結(jié)果:
上面的程序的sayHello和divided兩個(gè)方法都會(huì)拋出異常,但是sayHello方法中的異常將由該方法顯示捕捉,所以Spring AOP不會(huì)處理該異常。
而divide方法將拋出算術(shù)異常,且該異常沒(méi)有被任何程序所處理,故Spring AOP會(huì)對(duì)該異常進(jìn)行處理。
使用throwing屬性還有另一個(gè)作用:它可以用于限定切入點(diǎn)只匹配指定類型的異常。類似AfterRetuning的returning 屬性。
Spring AOP的AfterThrowing處理雖然可以對(duì)目標(biāo)方法的異常進(jìn)行處理,但是這種處理與直接使用catch捕捉是不同的:
catch意味完全處理該異常,如果catch沒(méi)有重新拋出一次異常,則方法可以正常結(jié)束,而AfterThrowing處理雖然處理了異常,但它不能完全處理異常,該異常依然會(huì)傳播到上一級(jí)調(diào)用者。
定義After增強(qiáng)處理:
After增強(qiáng)處理與AfterReturning增強(qiáng)處理有點(diǎn)相似,但是也有不同:
AfterRetuning只在目標(biāo)方法成功完成后才被織入。
After增強(qiáng)處理不管目標(biāo)方法如何結(jié)束,都會(huì)被織入。
因?yàn)橐粋€(gè)方法不管是如何結(jié)束,After增強(qiáng)處理它都會(huì)被織入,因此After增強(qiáng)處理必須處理正常返回和異常返回兩種情況,這種增強(qiáng)處理通常用于資源釋放。
使用@After Annotation標(biāo)注一個(gè)方法,即可將該方法轉(zhuǎn)成After增強(qiáng)處理,使用@After Annotation增強(qiáng)處理需要指定一個(gè)value屬性,該屬性值用于指定該增強(qiáng)處理被織入的切入點(diǎn),既可以是一個(gè)已有的切入點(diǎn)也可以直接指定切入點(diǎn)表達(dá)式。
@Aspect
publicclass AfterAdviceTest {
@After(”execution(* cn.huaxia.spring.*.*(。。))“)
publicvoid release(){
System.out.println(”模擬方法結(jié)束后資源釋放。。.。“);
}
}
結(jié)果:
從上面的輸出結(jié)果可以看出,After增強(qiáng)處理非常類似于異常處理中的finally塊的作用-----無(wú)論結(jié)果如何,它總在方法執(zhí)行結(jié)束之后被織入,因此特別適合進(jìn)行資源回收。
Around增強(qiáng)處理:
@Around Annotation用于標(biāo)注Around的增強(qiáng)處理,Around的增強(qiáng)處理是比較強(qiáng)的增強(qiáng)處理,它近似等于Before和AfterRetuning增強(qiáng)處理的總和,Around增強(qiáng)處理既可以在目標(biāo)方法之前執(zhí)行,也可以在執(zhí)行目標(biāo)方法之后織入增強(qiáng)動(dòng)作。
與Before增強(qiáng)處理和AfterReturning增強(qiáng)處理不同的是Around增強(qiáng)處理可以決定目標(biāo)方法執(zhí)行在什么時(shí)候執(zhí)行,如何執(zhí)行,甚至可以阻止目標(biāo)方法執(zhí)行。Around增強(qiáng)處理可以改變執(zhí)行目標(biāo)方法的參數(shù)值,還可以改變執(zhí)行目標(biāo)方法之后的返回值。
使用@Around Annotation時(shí)需要指定value屬性,該屬性用來(lái)指定該增強(qiáng)處理被織入的切入點(diǎn)。
當(dāng)定義一個(gè)Around增強(qiáng)處理方法時(shí),該方法的第一個(gè)形參必須是ProceedingJoinPoint類型(至少包含一個(gè)形參)。
在增強(qiáng)處理方法體內(nèi),調(diào)用ProceedingJoinPoint的process方法時(shí),還可以傳入一個(gè)Object[]對(duì)象,該數(shù)組中的值將被傳入目標(biāo)方法作為執(zhí)行方法的的實(shí)參。
-------這就是Around增強(qiáng)處理可以完全控制目標(biāo)方法執(zhí)行時(shí)機(jī)、如何執(zhí)行的關(guān)鍵。如果程序沒(méi)有調(diào)用ProceedingJoinPoint的proceed方法則目標(biāo)方法不會(huì)執(zhí)行。
@Aspect
publicclass AroundAdviceTest {
@Around(”execution(* cn.huaxia.spring.*.*(。。))“)
public Object proceedTX(ProceedingJoinPoint pre)throws Throwable {
System.out.println(”開始事務(wù)。。.。?!埃?
Object obj = pre.proceed(new String[] {”被修改的參數(shù)“ });
System.out.println(”結(jié)束事務(wù)。。.。?!埃?
return obj +”,新增的內(nèi)容“;
}
}
結(jié)果:
當(dāng)調(diào)用ProceedingJoinPonint的proceed方法時(shí),傳入的Object[]參數(shù)值將作為目標(biāo)方法的參數(shù),如果傳入的Object[]參數(shù)的長(zhǎng)度和目標(biāo)方法參數(shù)個(gè)數(shù)不匹配,或者Object[]數(shù)組元素與目標(biāo)方法所需的參數(shù)不匹配,程序就會(huì)拋出異常。
為了能獲取目標(biāo)方法參數(shù)的個(gè)數(shù)和類型,這就需要增強(qiáng)處理方法能夠訪問(wèn)執(zhí)行目標(biāo)方法的參數(shù)了。
訪問(wèn)目標(biāo)方法的參數(shù):
訪問(wèn)目標(biāo)方法最簡(jiǎn)單的的做法是定義增強(qiáng)處理方法時(shí)將第一個(gè)參數(shù)定義為JoinPoint類型,當(dāng)增強(qiáng)處理方法被調(diào)用時(shí),該JoinPoint參數(shù)就代表了織入增強(qiáng)處理的連接點(diǎn),JoinPoint包含如下幾個(gè)常用的方法:
Object[] getArgs():返回執(zhí)行目標(biāo)方法的參數(shù);
Signature getSignature():返回被增強(qiáng)的方法的相關(guān)信息;
Object getTarget():返回被織入增強(qiáng)處理的目標(biāo)方法;
Object getThis():返回AOP框架為目標(biāo)對(duì)象生成的代理對(duì)象。如:
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增強(qiáng)處理:執(zhí)行目標(biāo)方法前,執(zhí)行模擬開啟事務(wù)。。.。。.?!埃?
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增強(qiáng)處理:執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù)。。.。?!埃?
return obj + ”,新增加的內(nèi)容“;
}
@Before(”execution(* cn.huaxia.spring.service.*.*(。。))“)
public void authority(JoinPoint jp) {
System.out.println(”Before增強(qiáng):模擬權(quán)限檢查“);
System.out.println(”Before增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:“
+ jp.getSignature().getName());
System.out
.println(”Before增強(qiáng):目標(biāo)方法的參數(shù)為:“ + Arrays.toString(jp.getArgs()));
System.out.println(”Before增強(qiáng):被注入增強(qiáng)的處理 的目標(biāo)對(duì)象:“ + jp.getTarget());
}
@AfterReturning(pointcut=”execution(* cn.huaxia.spring.service.*.*(。。))“,returning=”obj“)
public void log(JoinPoint jp, Object obj) {
System.out.println(”AfterReturning增強(qiáng):獲取目標(biāo)方法的返回值:“ + obj);
System.out.println(”AfterReturning增強(qiáng):模擬日志記錄功能。。.。。“);
System.out.println(”AfterReturning增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:“
+ jp.getSignature().getName());
System.out.println(”AfterReturning增強(qiáng):目標(biāo)方法的參數(shù)為:“
+ Arrays.toString(jp.getArgs()));
System.out.println(”AfterReturning增強(qiáng):被注入增強(qiáng)的處理 的目標(biāo)對(duì)象:“ + jp.getTarget());
}
@After(”execution(* cn.huaxia.spring.service.*.*(。。))“)
public void release(JoinPoint jp) {
System.out.println(”After增強(qiáng):模擬方法結(jié)束后,資源釋放。。.。。“);
System.out.println(”After增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:“
+ jp.getSignature().getName());
System.out.println(”After增強(qiáng):目標(biāo)方法的參數(shù)為:“ + Arrays.toString(jp.getArgs()));
System.out.println(”After增強(qiáng):被注入增強(qiáng)的處理 的目標(biāo)對(duì)象:“ + jp.getTarget());
}
}
輸出結(jié)果:
Around增強(qiáng)處理:執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù)。。.。。
AfterReturning增強(qiáng):獲取目標(biāo)方法的返回值:被修改的參數(shù),新增加的內(nèi)容
AfterReturning增強(qiáng):模擬日志記錄功能。。.。。
AfterReturning增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:sayHello
AfterReturning增強(qiáng):目標(biāo)方法的參數(shù)為:[被修改的參數(shù)]
AfterReturning增強(qiáng):被注入增強(qiáng)的處理的目標(biāo)對(duì)象:cn.huaxia.spring.service.Chinese2@941db6
After增強(qiáng):模擬方法結(jié)束后,資源釋放。。.。。
After增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:sayHello
After增強(qiáng):目標(biāo)方法的參數(shù)為:[被修改的參數(shù)]
After增強(qiáng):被注入增強(qiáng)的處理的目標(biāo)對(duì)象:cn.huaxia.spring.service.Chinese2@941db6
被修改的參數(shù),新增加的內(nèi)容
Around增強(qiáng)處理:執(zhí)行目標(biāo)方法前,執(zhí)行模擬開啟事務(wù)。。.。。.。
Before增強(qiáng):模擬權(quán)限檢查
Before增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:eat
Before增強(qiáng):目標(biāo)方法的參數(shù)為:[被修改的參數(shù)]
Before增強(qiáng):被注入增強(qiáng)的處理的目標(biāo)對(duì)象:cn.huaxia.spring.service.Chinese2@941db6
我正在吃:被修改的參數(shù)
Around增強(qiáng)處理:執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù)。。.。。
AfterReturning增強(qiáng):獲取目標(biāo)方法的返回值:null,新增加的內(nèi)容
AfterReturning增強(qiáng):模擬日志記錄功能。。.。。
AfterReturning增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:eat
AfterReturning增強(qiáng):目標(biāo)方法的參數(shù)為:[被修改的參數(shù)]
AfterReturning增強(qiáng):被注入增強(qiáng)的處理的目標(biāo)對(duì)象:cn.huaxia.spring.service.Chinese2@941db6
After增強(qiáng):模擬方法結(jié)束后,資源釋放。。.。。
After增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:eat
After增強(qiáng):目標(biāo)方法的參數(shù)為:[被修改的參數(shù)]
After增強(qiáng):被注入增強(qiáng)的處理的目標(biāo)對(duì)象:cn.huaxia.spring.service.Chinese2@941db6
Around增強(qiáng)處理:執(zhí)行目標(biāo)方法前,執(zhí)行模擬開啟事務(wù)。。.。。.。
Before增強(qiáng):模擬權(quán)限檢查
Before增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:divide
Before增強(qiáng):目標(biāo)方法的參數(shù)為:[]
Before增強(qiáng):被注入增強(qiáng)的處理的目標(biāo)對(duì)象:cn.huaxia.spring.service.Chinese2@941db6
After增強(qiáng):模擬方法結(jié)束后,資源釋放。。.。。
After增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:divide
After增強(qiáng):目標(biāo)方法的參數(shù)為:[]
After增強(qiáng):被注入增強(qiáng)的處理的目標(biāo)對(duì)象:cn.huaxia.spring.service.Chinese2@941db6
Spring AOP采用和AsoectJ一樣的優(yōu)先順序來(lái)織入增強(qiáng):在”進(jìn)入“連接點(diǎn)時(shí),最高等級(jí)的增強(qiáng)處理將先織入(所以給定兩個(gè)Before增強(qiáng)處理中,優(yōu)先級(jí)高的那個(gè)會(huì)執(zhí)行),在”退出“連接點(diǎn)時(shí),最高優(yōu)先等級(jí)的增強(qiáng)處理將會(huì)最后織入。
4個(gè)增強(qiáng)處理的優(yōu)先等級(jí)如下(從低到高):
Before增強(qiáng)處理-------------》Around增強(qiáng)處理-------------》AfterReturning增強(qiáng)處理-------------》After增強(qiáng)處理
當(dāng)兩個(gè)不同的兩個(gè)增強(qiáng)處理需要在同一個(gè)連接點(diǎn)被織入時(shí),Spring AOP將以隨機(jī)的順序來(lái)織入這兩個(gè)增強(qiáng)處理,如果應(yīng)用需要指定不同切面里增強(qiáng)處理的優(yōu)先級(jí),Spring提供了兩個(gè)解決方案:
讓切面類實(shí)現(xiàn)org.springframework.core.Ordered接口,實(shí)現(xiàn)該接口提供的 int getOrder()方法,方法返回值越小,則優(yōu)先級(jí)越高。
直接使用@Order Annotation 來(lái)標(biāo)注一個(gè)切面類,使用@Order Annotation時(shí)可以指定一個(gè)int 型的value屬性,該屬性值越小,優(yōu)先等級(jí)越高。
當(dāng)相同的切面里的兩個(gè)增強(qiáng)處理需要在相同的連接點(diǎn)被織入時(shí),Spring AOP將以隨機(jī)的方式來(lái)織入這兩個(gè)增強(qiáng)處理,沒(méi)有辦法指定他們的順序,如果一定要它們以特定的順序被織入,則可以考慮把它們壓縮到一個(gè)增強(qiáng)處理中,或者是把它們分別放在不同的切面,在通過(guò)切面的優(yōu)先等級(jí)來(lái)排序
如果只要訪問(wèn)目標(biāo)方法的參數(shù),Spring還提供了一個(gè)更加簡(jiǎn)單的方式:我們可以在程序中使用args來(lái)綁定目標(biāo)方法的參數(shù),如果args表達(dá)是中指定一個(gè)或多個(gè)參數(shù),則該切入點(diǎn)只匹配具有對(duì)應(yīng)形參的方法,且目標(biāo)方法的參數(shù)值將被出入增強(qiáng)處理方法。
如:
@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(”目標(biāo)方法中的String參數(shù)“ + food);
System.out.println(”目標(biāo)方法的time參數(shù)“ + time);
System.out.println(”模擬日志記錄功能。。.。。.“);
System.out.println(”目標(biāo)參數(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)在的時(shí)間是:“ + time);
}
}
結(jié)果:
為什么目標(biāo)方法的返回值是null,因?yàn)樵撉腥朦c(diǎn)只匹配 publicvoid eat(String food, Date time)方法。
從上面可以得出,使用args表達(dá)式有如下兩個(gè)作用:
--提供更簡(jiǎn)單的方式訪問(wèn)目標(biāo)方法的參數(shù);
--可用于對(duì)切入表達(dá)式增加額外限制。
除此之外,使用args表達(dá)式時(shí)還可以使用如下形式:args(name,age,。。),這表明增強(qiáng)處理方法中可通過(guò)name,age來(lái)訪問(wèn)目標(biāo)方法的參數(shù),上面表達(dá)式括號(hào)中的2點(diǎn),它表示可匹配更多的參數(shù),如:
public void doSomething(String name,int age)
這意味著只要目標(biāo)方法第一個(gè)參數(shù)是String類型,第二個(gè)參數(shù)是int則該方法就可以匹配該切入點(diǎn)。
定義切入點(diǎn):
正如前面的FourAdviceTest程序中看到的,這個(gè)切面類定義了4個(gè)增強(qiáng)處理,這4個(gè)增強(qiáng)處理分別指定了相同的切入點(diǎn)表達(dá)式,這種做法顯然不符合軟件設(shè)計(jì)的原則:我們將那個(gè)切入點(diǎn)表達(dá)式重復(fù)了4次,如果需要該這個(gè)切入點(diǎn),那么就要修改4處。
Spring AOP只支持以Spring Bean的方法執(zhí)行組作為連接點(diǎn),
例如:
@Pointcut(”execution(* transfer(。。))“)
private void anyOldTransfer(){}一旦采用上面的代碼片段定義了名為anyOldTrandser的切入點(diǎn)之后,程序就可以重復(fù)使用該切入點(diǎn)了,甚至可以在其他切面類、其他包的切面類里使用該切入點(diǎn),不過(guò)這取決于該方法簽名前的訪問(wèn)修飾符。
切入點(diǎn)指示符:
正如前面的execution就是一個(gè)切入點(diǎn)指示符,Spring AOP僅僅支持部分AspectJ切入點(diǎn)指示符,不僅如此Spring AOP只支持使用方法調(diào)用作為連接點(diǎn),所以Spring AOP的切入點(diǎn)指示符僅匹配方法執(zhí)行的連接點(diǎn)。
Spring AOP一共支持如下幾種切入點(diǎn)指示符:
----execution:用于匹配執(zhí)行方法的連接點(diǎn),是Spring AOP最主要的切入點(diǎn)指示符,execution表達(dá)式的格式如下:
execution(modifies-pattern? ret-type-pattern declaring-type-parttern? name--pattern(parm-pattern) throws-pattern?)
以上打了問(wèn)號(hào)的都可以省略。
上面格式中的execution是不變的,用于作為execution表達(dá)式的開頭,整個(gè)表示式各個(gè)部分的解釋為:
modifies-pattern:指定方法的修飾符,支持通配符,該部分可以省略。
ret-type-pattern:指定方法的返回值類型,支持通配符,可以使用“*”通配符來(lái)匹配所有返回值類型。
declaring-type-parttern:指定方法所屬的類,支持通配符,該部分可以省略。
name--pattern:指定匹配指定方法名,支持通配符,可以使用“*”通配符來(lái)匹配所有方法。
parm-pattern:指定方法聲明中的形參列表,支持兩個(gè)通配符:“*”、“。。”,其中*表示一個(gè)任意類型的參數(shù),而“。?!北硎玖銈€(gè)或多個(gè)任意類型的參數(shù)。
throws-pattern:指定方法聲明拋出的異常,支持通配符,該部分可以省略。
例如下面幾個(gè)execution表達(dá)式:
//匹配任意public方法的執(zhí)行。
execution(public * * (。。))
//匹配任意方法名以set開始的方法只想能夠。
execution(* set* (。。))
//匹配AccountService里定義的任意方法的執(zhí)行。
execution(* org.huaxia.AccountService.* (。。))
//匹配Service包中任意類的任意方法的執(zhí)行。
execution(* org.huaxia.service.*.*(。。))
?。瓀ithin:限定匹配特定類型的連接點(diǎn),當(dāng)使用Spring AOP的時(shí)候,只能匹配方法執(zhí)行的連接點(diǎn),
例如下面幾個(gè)within表達(dá)式:
//在Service包中的任意連接點(diǎn)。
within(* org,huaxia.service.*)
//在Service包或者子包的任意連接點(diǎn)
within(* org.huaxia.service..*)
----this:用于限定AOP代理必須指定類型的實(shí)例,用于匹配該對(duì)象的所有連接點(diǎn)。當(dāng)使用Spring AOP的時(shí)候,只能匹配方法執(zhí)行的連接點(diǎn)。
例如:
//匹配實(shí)現(xiàn)了AccountService接口的代理對(duì)象的所有連接點(diǎn)
this(org.huaxia.service.AccountService)
?。璽arget:用于限定目標(biāo)對(duì)象必須是指定類型的實(shí)例,用于匹配該對(duì)象的所有連接點(diǎn),當(dāng)使用Spring AOP只匹配執(zhí)行方法的連接點(diǎn)。例如“”
//匹配實(shí)現(xiàn)了AccountService接口的目標(biāo)對(duì)象的所有連接點(diǎn)
target(org.huaxia.service.AccountService)
?。璦rgs:用于對(duì)連接點(diǎn)的參數(shù)類型進(jìn)行限制,要求參數(shù)類型是指定類型的實(shí)例,例如:
//匹配只接受一個(gè)參數(shù),且傳入的參數(shù)類型是Serializable的所有連接點(diǎn)
args(java.io.Serializable)
注意:該例中給出的切入點(diǎn)表達(dá)式與execution(* *(java.io.Serializable))不同:args版本只匹配動(dòng)態(tài)運(yùn)行時(shí)傳入?yún)?shù)值是Serializable類型的情況,而execution版本只匹配方法簽名只包含一個(gè)Serializable類型的參數(shù)的方法。
----bean:用于指定只匹配指定Bean實(shí)例內(nèi)連接點(diǎn),實(shí)際上只能使用方法執(zhí)行作為連接點(diǎn),定義bean表達(dá)式需要傳入id或name,表示只匹配Bean實(shí)例內(nèi)連接點(diǎn),支持“*”通配符。
例如:
//匹配tradeService Bean實(shí)例內(nèi)方法執(zhí)行的連接點(diǎn)
bean(tradeService)
//匹配名字以Service結(jié)尾的Bean實(shí)例內(nèi)方法執(zhí)行的連接點(diǎn)。
bean(*Service)
組合切入點(diǎn)表達(dá)式:
Spring支持3中邏輯運(yùn)算符來(lái)組合切入點(diǎn)表達(dá)式:
&&:要求連接點(diǎn)同時(shí)匹配兩個(gè)切入點(diǎn)表達(dá)式;
||:只要連接點(diǎn)匹配任意一個(gè)切入點(diǎn)表達(dá)式;
?。。阂筮B接點(diǎn)不匹配指定切入點(diǎn)表達(dá)式。
⑤AOP 基于配置Xml文件的管理方式
除了前面介紹的基于JDK1.5的Annotation方式來(lái)定義切面、切入點(diǎn)和增強(qiáng)處理,Spring AOP也允許使用XML文件來(lái)定義管理它們。
實(shí)際上,使用XML定義AOP也是@AspectJ一樣的同樣需要指定相關(guān)數(shù)據(jù):配置切面、切入點(diǎn)、增強(qiáng)處理所需要的信息完全一樣,只是提供這些信息的位置不一樣而已。使用XMLA文件配置AOPd的方式有很多優(yōu)點(diǎn)但是也有一些缺點(diǎn):
xml配置方式比@AspectJ方式有更多的限制:僅支持“singleton”切面的Bean,不能在xml中組合多個(gè)命名連接點(diǎn)的聲明。
在Spring的配置文件中,所有的切面、切入點(diǎn)和增強(qiáng)處理都必須定義在《aop:config.。/》元素內(nèi)部?!禸eans.。/》元素下可以包含多個(gè)《aop:config.。/》元素。
一個(gè)《aop:config.。/》可以包含多個(gè)pointcut、advisor和aspect元素,且這3個(gè)元素必須按照此順序類定義。
注意:當(dāng)我們使用《aop:config.。/》方式進(jìn)行配置時(shí),可能與Spring的自動(dòng)代理方式?jīng)_突,例如我們使用BeanNameAutoProxyCreator或類似的方式顯示啟用了自動(dòng)代理,則它可能導(dǎo)致問(wèn)題(例如有些增請(qǐng)?zhí)幚頉](méi)有被織入)因此要么全部使用自動(dòng)代理的方式,要么全部使用《aop:config.。/》配置方式。不要不兩者混合使用。
————配置切面:
定義切面使用《aop:aspect.。/》元素,使用該元素來(lái)定義切面時(shí),其實(shí)質(zhì)是將一個(gè)已有的Spring Bean轉(zhuǎn)換成切面Bean。
因?yàn)榍忻鍮ean可以當(dāng)成一個(gè)普通的SpringBean來(lái)配置,所以可以為該切面Bean配置依賴注入。
配置《aop:aspect.。/》元素時(shí)可以指定如下3個(gè)屬性:
id:定義該切面的標(biāo)識(shí)名;
ref:指定以指定ref所引用的的普通Bean作為切面Bean。
order:指定該切面Bean的優(yōu)先等級(jí),數(shù)字越小,等級(jí)越大。
————配置增強(qiáng)處理:
《aop:before.。。/》:Before增強(qiáng)處理
《aop:after.。/》:After增強(qiáng)處理
《aop:after-returning.。。/》:afterReturning增強(qiáng)處理
《aop:after-throwing.。/》:afterThrowing增強(qiáng)處理
《aop:around.。。/》:Around增強(qiáng)處理
上面的元素不能配置子元素,但可以配置如下屬性:
pointcut:該屬性指定一個(gè)切入點(diǎn)表達(dá)式,Spring將在匹配該表達(dá)式的連接點(diǎn)時(shí)織入增強(qiáng)處理。
pointcut-ref:該屬性指定一個(gè)已經(jīng)存在的切入點(diǎn)的 名稱,通常pointcut與pointcut-ref只需要使用其中的一個(gè)。
method:該屬性指定一個(gè)方法名,指定切面Bean的該方法將作為增強(qiáng)處理。
throwing:該屬性只對(duì)《aop:after-throwing.。/》起作用,用于指定一個(gè)形參名,afterThrowing增強(qiáng)處理方法可以通過(guò)該形參訪問(wèn)目標(biāo)方法所拋出的異常。
returning:該屬性只對(duì)《aop:after-returning.。。/》起作用,用于指定一個(gè)形參名,afterReturning增強(qiáng)處理方法可以通過(guò)該形參訪問(wèn)目標(biāo)方法的返回值。
當(dāng)定義切入點(diǎn)表達(dá)式時(shí),XML文件配置方式和@AspectJ Annotation方式支持完全相同的切入點(diǎn)指示符,一樣支持execution、within、args、this、target和bean等切入點(diǎn)指示符。
XML配置文件方式和@AspectJ Annotation方式一樣支持組合切入點(diǎn)表達(dá)式,但XML配置方式不再使用簡(jiǎn)單的&&、||、!作為組合運(yùn)算符,而是使用and(相當(dāng)于&&)、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》
《!-- 定義一個(gè)普通組件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è)務(wù)類:
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增強(qiáng)處理:執(zhí)行目標(biāo)方法前,執(zhí)行模擬開啟事務(wù)。。.。。.。“);
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增強(qiáng)處理:執(zhí)行目標(biāo)方法之后,模擬結(jié)束事務(wù)。。.。?!埃?
return obj +”,新增加的內(nèi)容“;
}
publicvoid authority(JoinPoint jp) {
System.out.println(”Before增強(qiáng):模擬權(quán)限檢查“);
System.out.println(”Before增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:“
+ jp.getSignature().getName());
System.out.println(”Before增強(qiáng):目標(biāo)方法的參數(shù)為:“ + Arrays.toString(jp.getArgs()));
System.out.println(”Before增強(qiáng):被注入增強(qiáng)的處理的目標(biāo)對(duì)象:“ + jp.getTarget());
}
publicvoid log(JoinPoint jp, Object obj) {
System.out.println(”AfterReturning增強(qiáng):獲取目標(biāo)方法的返回值:“ + obj);
System.out.println(”AfterReturning增強(qiáng):模擬日志記錄功能。。.。。“);
System.out.println(”AfterReturning增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:“
+ jp.getSignature().getName());
System.out.println(”AfterReturning增強(qiáng):目標(biāo)方法的參數(shù)為:“
+ Arrays.toString(jp.getArgs()));
System.out.println(”AfterReturning增強(qiáng):被注入增強(qiáng)的處理的目標(biāo)對(duì)象:“ + jp.getTarget());
}
publicvoid release(JoinPoint jp) {
System.out.println(”After增強(qiáng):模擬方法結(jié)束后,資源釋放。。.。?!埃?
System.out.println(”After增強(qiáng):被織入增強(qiáng)處理的目標(biāo)方法:“
+ jp.getSignature().getName());
System.out.println(”After增強(qiáng):目標(biāo)方法的參數(shù)為:“ + Arrays.toString(jp.getArgs()));
System.out.println(”After增強(qiáng):被注入增強(qiáng)的處理的目標(biāo)對(duì)象:“ + jp.getTarget());
}
}
輸出結(jié)果:
————配置切入點(diǎn):
類似于@AspectJ方式,允許定義切入點(diǎn)來(lái)重用切入點(diǎn)表達(dá)式,XML配置方式也可以通過(guò)定義切入點(diǎn)來(lái)重用切入點(diǎn)表達(dá)式。
配置《aop:pointcut.。/》元素時(shí)通常需要指定如下兩個(gè)屬性:
id:指定該切入點(diǎn)的標(biāo)識(shí)名;
expression:指定該切入點(diǎn)關(guān)聯(lián)的切入點(diǎn)表達(dá)式。
如下面代碼:
《aop:pointcut id=”myPointcut“ expression=”execution(* lee.*.*(。。))“/》
除此之外,如果程序已經(jīng)使用Annotation定義了切入點(diǎn),在《aop:pointcut 。。/》元素中指定切入點(diǎn)表達(dá)式時(shí)還有另一種用法:
《aop:pointcut expression=”org.huaxia.SystemArchitecture.myPointcut()“》
5,Spring的事務(wù):
Spring事務(wù)管理不需要與任何特定事務(wù)API耦合,對(duì)不同的持久化層訪問(wèn)技術(shù),編程式事務(wù)提供一致事務(wù)編程風(fēng)格,通過(guò)模板化的操作一致性的管理事務(wù)。聲明式事務(wù)基于Spring AOP實(shí)現(xiàn),并不需要程序開發(fā)人員成為AOP專家,亦可以輕易使用Spring的聲明式事務(wù)管理。
Spring支持的事務(wù)策略:
JavaEE應(yīng)用程序的傳統(tǒng)事務(wù)有兩種策略,全局事務(wù)和局部事務(wù)。全局事務(wù)由應(yīng)用服務(wù)器管理,需要底層的服務(wù)器的JTA支持,局部事務(wù)和底層采用的持久化技術(shù)有關(guān),當(dāng)采用JDBC持久化技術(shù)時(shí),需要采用Connection對(duì)象來(lái)操作事務(wù);而采用Hibernate持久化技術(shù)時(shí),需要使用session操作失誤。
當(dāng)采用傳統(tǒng)的事務(wù)編程策略時(shí),程序代碼必然和具體的事務(wù)操作代碼耦合。這樣的后果是:當(dāng)應(yīng)用程序需要在不同的的事務(wù)策略之間切換時(shí),開發(fā)者必須手動(dòng)修改程序代碼。當(dāng)改為Spring操作事務(wù)策略時(shí),即可改變這種狀況。
Spring事務(wù)策略是通過(guò)PlatformTransactionManager接口體現(xiàn)的,該接口是Spring事務(wù)策略的核心。
即使使用容器管理的JTA,代碼依然不需要JNDI查找,無(wú)需與特定的JTA資源耦合在一起,通過(guò)配置文件,JTA資源傳給PlatformTransactionManager的實(shí)現(xiàn)類,因此,程序的代碼可以在JTA事務(wù)管理和非JTA事務(wù)管理之間輕松的切換。
Spring是否支持事務(wù)跨多個(gè)數(shù)據(jù)庫(kù)資源?
答:Spring完全支持這種跨多個(gè)事務(wù)性資源的全局事務(wù),前提是底層的應(yīng)用服務(wù)器(如WebLogic、WebSphere等)支持JTA全局事務(wù),可以這樣說(shuō):Spring本身沒(méi)有任何事務(wù)支持,它只是負(fù)責(zé)包裝底層的事務(wù)————當(dāng)我們?cè)诔绦蛑忻嫦騊latformTransactionManager接口編程時(shí),Spring在底層負(fù)責(zé)將這些操作轉(zhuǎn)換成具體的事務(wù)操作代碼,所以應(yīng)用底層支持什么樣的事務(wù)策略,那么Spring就支持什么樣的事務(wù)策略。Spring事務(wù)管理的優(yōu)勢(shì)是將應(yīng)用從具體的事務(wù)API中分離出來(lái),而不是真正提供事務(wù)管理的底層實(shí)現(xiàn)。
Spring的具體的事務(wù)管理由PlatformTransactionManager的不同實(shí)現(xiàn)類來(lái)完成,在Spring容器中配置PlatformTransactionManager時(shí)必須針對(duì)不同的環(huán)境提供不同的實(shí)現(xiàn)類,下面提供了不同的持久化訪問(wèn)環(huán)境,及其對(duì)應(yīng)的PlatformTransactionManager實(shí)現(xiàn)類的配置。
1,BC數(shù)據(jù)源的局部事務(wù)策略的配置文件如下:
《!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實(shí)現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 --》
《property name=”maxIdleTime“ value=”20“/》
《/bean》
《!-- 配置JDBC數(shù)據(jù)源的局部事務(wù)管理器,使用DataSourceTransactionManager 類 --》
《!-- 該類實(shí)現(xiàn)PlatformTransactionManager接口,是針對(duì)采用數(shù)據(jù)源連接的特定實(shí)現(xiàn)--》
《bean id=”transactionManager“
class=”org.springframework.jdbc.datasource.DataSourceTransactionManager“》
《!-- 配置DataSourceTransactionManager時(shí)需要依注入DataSource的引用 --》
《property name=”dataSource“ ref=”dataSource“/》
《/bean》
,2,器管理JTA全局事務(wù)的配置文件如下:
《!-- 配置JNDI數(shù)據(jù)源Bean--》
《bean id=”dateSource“ class=”org.springframework.jndi.JndiObjectFactoryBean“》
《!--容器管理數(shù)據(jù)源的JNDI--》
《property name=”jndiName“ value=”jdbc/jpetstore“/》
《/bean》
《!--使用JtaTransactionManager類,該類實(shí)現(xiàn)PlatformTransactionManager接口--》
《!--針對(duì)采用全局事務(wù)管理的特定實(shí)現(xiàn)--》
《bean id=”transactionManager“ class=”org.springframework.transaction.jta.JtaTransactionManager“/》
3,采用Hibernate持久層訪問(wèn)策略時(shí),局部事務(wù)的策略的配置文件如下:
《!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實(shí)現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 --》
《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屬性用來(lái)列出全部映射文件 --》
《property name=”mappingResources“》
《list》
《!-- 以下用來(lái)列出Hibernate映射文件 --》
《value》Person.hbm.xml《/value》
《/list》
《/property》
《!-- 定義Hibernate的SessionFactory的屬性 --》
《property name=”hibernateProperties“》
《props》
《!-- 指定數(shù)據(jù)庫(kù)方言 --》
《prop key=”hibernate.dialect“》
org.hibernate.dialect.MySQLInnoDBDialect《/prop》
《!-- 是否根據(jù)需要每次自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù) --》
《prop key=”hibernate.hbm2ddl.auto“》update《/prop》
《!-- 顯示Hibernate持久化操作所生成的SQL --》
《prop key=”hibernate.show_sql“》true《/prop》
《!-- 將SQL腳本進(jìn)行格式化后再輸出 --》
《prop key=”hibernate.format_sql“》true《/prop》
《/props》
《/property》
《/bean》
《!--配置Hibernate局部事務(wù)管理器,使用HibernateTransactionManager類--》
《!--該類是PlatformTransactionManager接口針對(duì)采用Hibernate的特定實(shí)現(xiàn)類--》
《bean id=”transactionManager“ class=”org.springframework.orm.hibernate3.HibernateTransactionManager“》
《property name=”sessionFactory“ ref=”sessionFactory“/》
《/bean》
4,底層采用Hibernate持久化技術(shù),但事務(wù)依然采用JTA全局事務(wù):
《!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實(shí)現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 --》
《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屬性用來(lái)列出全部映射文件 --》
《property name=”mappingResources“》
《list》
《!-- 以下用來(lái)列出Hibernate映射文件 --》
《value》Person.hbm.xml《/value》
《/list》
《/property》
《!-- 定義Hibernate的SessionFactory的屬性 --》
《property name=”hibernateProperties“》
《props》
《!-- 指定數(shù)據(jù)庫(kù)方言 --》
《prop key=”hibernate.dialect“》
org.hibernate.dialect.MySQLInnoDBDialect《/prop》
《!-- 是否根據(jù)需要每次自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù) --》
《prop key=”hibernate.hbm2ddl.auto“》update《/prop》
《!-- 顯示Hibernate持久化操作所生成的SQL --》
《prop key=”hibernate.show_sql“》true《/prop》
《!-- 將SQL腳本進(jìn)行格式化后再輸出 --》
《prop key=”hibernate.format_sql“》true《/prop》
《/props》
《/property》
《/bean》
《!--使用JtaTransactionManager類,該類實(shí)現(xiàn)PlatformTransactionManager接口--》
《!--針對(duì)采用全局事務(wù)管理的特定實(shí)現(xiàn)--》
《bean id=”transactionManager“ class=”org.springframework.transaction.jta.JtaTransactionManager“/》
從上面的配置文件可以看出,當(dāng)使用Spring事務(wù)管理事務(wù)策略時(shí),應(yīng)用程序無(wú)需與具體的事務(wù)策略耦合,,Spring提供了兩種事務(wù)管理方式:
編程式管理方式:即使使用Spring編程式事務(wù)時(shí),程序也可直接獲取容器中的TransactionManager Bean,該Bean總是PlatformTransactionManager的實(shí)例,可以通過(guò)該接口提供的3個(gè)方法來(lái)開始事務(wù),提交事務(wù)和回滾事務(wù)。
聲明式管理方式:無(wú)需在Java程序中書寫任何的事務(wù)操作代碼,而是通過(guò)在XML文件中為業(yè)務(wù)組件配置事務(wù)代理(AOP代理的一種),AOP事務(wù)代理所織入的增強(qiáng)處理也是由Spring提供:在目標(biāo)方法前,織入開始事務(wù);在目標(biāo)方法后執(zhí)行,織入結(jié)束事務(wù)。
注意:
Spring編程事務(wù)還可以通過(guò)TransactionTemplate類來(lái)完成,該類提供了一個(gè)execute方法,可以更簡(jiǎn)潔的方式來(lái)進(jìn)行事務(wù)操作。
當(dāng)使用聲明式事務(wù)時(shí),開發(fā)者無(wú)需書寫任何事務(wù)管理代碼,不依賴Spring或或任何事務(wù)API,Spring的聲明式事務(wù)編程無(wú)需任何額外的容器支持,Spring容器本身管理聲明式事務(wù),使用聲明式事務(wù)策略,可以讓開發(fā)者更好的專注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。
————使用TransactionProxyFactoryBean創(chuàng)建事務(wù)代理————
在Spring1.X,聲明事務(wù)使用TransactionProxyFactoryBean來(lái)配置事務(wù)代理Bean,正如他的名字所暗示的,它是一個(gè)工廠Bean,該工程Bean專為目標(biāo)Bean生成事務(wù)代理Bean,既然TransactionProxyFactoryBean產(chǎn)生的是事務(wù)代理Bean,可見Spring的聲明式事務(wù)策略是基于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ù)源實(shí)現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 --》
《property name=”maxIdleTime“ value=”20“/》
《/bean》
《!-- 配置JDBC數(shù)據(jù)源的局部事務(wù)管理器,使用DataSourceTransactionManager 類 --》
《!-- 該類實(shí)現(xiàn)PlatformTransactionManager接口,是針對(duì)采用數(shù)據(jù)源連接的特定實(shí)現(xiàn)--》
《bean id=”transactionManager“
class=”org.springframework.jdbc.datasource.DataSourceTransactionManager“》
《!-- 配置DataSourceTransactionManager時(shí)需要依注入DataSource的引用 --》
《property name=”dataSource“ ref=”dataSource“/》
《/bean》
《!-- 配置一個(gè)業(yè)務(wù)邏輯Bean --》
《bean id=”test“ class=”lee.TestImpl“》
《property name=”ds“ ref=”dataSource“/》
《/bean》
《!-- 為業(yè)務(wù)邏輯Bean配置事務(wù)代理 --》
《bean id=”testTrans“ class=
”org.springframework.transaction.interceptor.TransactionProxyFactoryBean“》
《!-- 為事務(wù)代理工廠Bean注入事務(wù)管理器 --》
《property name=”transactionManager“ ref=”transactionManager“/》
《property name=”target“ ref=”test“/》
《!-- 指定事務(wù)屬性 --》
《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 + ”’)“);
//如果增加事務(wù)控制,我們發(fā)現(xiàn)第一條記錄也插不進(jìn)去。
//如果沒(méi)有事務(wù)控制,則第一條記錄可以被插入
}
}
配置事務(wù)代理的時(shí)。需要傳入一個(gè)事務(wù)管理器,一個(gè)目標(biāo)Bena,并指定該事務(wù)的事務(wù)屬性,事務(wù)實(shí)行由transactionAttribute指定,上面只有一條事務(wù)傳播規(guī)則,該規(guī)則指定對(duì)于所有方法都使用PROPAGATION_REQUIRED的傳播屬性,Spring支持的事務(wù)傳播規(guī)則:
PROPAGATION_MANDATORY:要求調(diào)用該方法的線程已處于事務(wù)環(huán)境中,否則拋出異常;
PROPAGATION_NESTED:如果執(zhí)行該方法的線程已處于事務(wù)的環(huán)境下,依然啟動(dòng)新的事務(wù),方法在嵌套的事務(wù)里執(zhí)行,如果執(zhí)行該方法的線程并未處于事務(wù)的環(huán)境下,也啟動(dòng)新的事務(wù),此時(shí)與PROPAGATION_REQUIRED;
PROPAGATION_NOT_SUPPORTED:如果調(diào)用該方法的線程處于事務(wù)中,則暫停事務(wù),再執(zhí)行方法。
PROPAGATION_NEVER:不允許執(zhí)行該方法的線程處于事務(wù)的環(huán)境下,如果執(zhí)行該方法的線程處于事務(wù)的環(huán)境下,則會(huì)拋出異常;
PROPAGATION_REQUIRED:要求在事務(wù)環(huán)境中執(zhí)行該方法,如果當(dāng)前執(zhí)行線程已處于事務(wù)中,則直接調(diào)用;否則,則啟動(dòng)新的事務(wù)后執(zhí)行該方法。
PROPAGATION_REQUIREDS_NEW:該方法要求在新的事務(wù)中執(zhí)行,如果當(dāng)前執(zhí)行線程已處于事務(wù)環(huán)境下,則暫停當(dāng)前事務(wù),啟動(dòng)新事務(wù)后執(zhí)行方法;否則,啟動(dòng)新的事務(wù)后執(zhí)行方法。
PROPAGATION_SUPPORTS:如果當(dāng)前線程處于事務(wù)中,則使用當(dāng)前事務(wù),否則不使用事務(wù)。
主程序代碼:
public class MainTest
{
public static void main(String[] args)
{
//創(chuàng)建Spring容器
ApplicationContext ctx = new
ClassPathXmlApplicationContext(”bean.xml“);
//獲取事務(wù)代理Bean
Test t = (Test)ctx.getBean(”testTrans“);----------①
//執(zhí)行插入操作
t.insert(”bbb“);
}
}
上面①處代碼獲取testTrans Bean,該Bean已不再是TestImpl的實(shí)例了,它只是Spring容器創(chuàng)建的事務(wù)代理,該事務(wù)代理以TestImpl實(shí)例為目標(biāo)對(duì)象,且該目標(biāo)對(duì)象也實(shí)現(xiàn)了Test接口,故代理對(duì)象也可當(dāng)作Test實(shí)例使用。
實(shí)際上,Spring不僅支持對(duì)接口的代理,整合GBLIB后,Spring甚至可以對(duì)具體類生成代理,只要設(shè)置proxyTargetClass屬性為true就可以了,如果目標(biāo)Bean沒(méi)有實(shí)現(xiàn)任何接口,proxyTargetClass默認(rèn)為true,此時(shí)Spring會(huì)對(duì)具體的類生成代理。當(dāng)然通常建議面向接口編程,而不面向具體的實(shí)現(xiàn)類編程。
————Spring2.X的事務(wù)配置策略————
上面介紹的TransactionProxyFactoryBean配置策略簡(jiǎn)單易懂,但是配置起來(lái)極為繁瑣:每個(gè)目標(biāo)Bean都需要額外配置一個(gè)TransactionProxyFactoryBean代理,這種方法將導(dǎo)致配置文件的急劇增加。
Spring2.X的XML Schema方式提供了更簡(jiǎn)潔事務(wù)配置策略,Spring提供了tx命名空間來(lái)配置事務(wù)管理,tx命名空間下提供了《tx:advice.。/》元素來(lái)配置事務(wù)切面,一旦使用了該元素配置了切面,就可以直接使用《aop:advisor.。。/》元素啟動(dòng)自動(dòng)代理。
如:
《?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ù)源實(shí)現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 --》
《property name=”maxIdleTime“ value=”20“/》
《/bean》
《!-- 配置JDBC數(shù)據(jù)源的局部事務(wù)管理器,使用DataSourceTransactionManager 類 --》
《!-- 該類實(shí)現(xiàn)PlatformTransactionManager接口,是針對(duì)采用數(shù)據(jù)源連接的特定實(shí)現(xiàn)--》
《bean id=”transactionManager“
class=”org.springframework.jdbc.datasource.DataSourceTransactionManager“》
《!-- 配置DataSourceTransactionManager時(shí)需要依注入DataSource的引用 --》
《property name=”dataSource“ ref=”dataSource“/》
《/bean》
《!-- 配置一個(gè)業(yè)務(wù)邏輯Bean --》
《bean id=”test“ class=”lee.TestImpl“》
《property name=”ds“ ref=”dataSource“/》
《/bean》
《!-- 配置事務(wù)切面Bean,指定事務(wù)管理器 --》
《tx:advice id=”txAdvice“ transaction-manager=”transactionManager“》
《!-- 用于配置詳細(xì)的事務(wù)語(yǔ)義 --》
《tx:attributes》
《!-- 所有以‘get’開頭的方法是read-only的 --》
《tx:method name=”get*“ read-only=”true“/》
《!-- 其他方法使用默認(rèn)的事務(wù)設(shè)置 --》
《tx:method name=”*“/》
《/tx:attributes》
《/tx:advice》
《aop:config》
《!-- 配置一個(gè)切入點(diǎn),匹配lee包下所有以Impl結(jié)尾的類
執(zhí)行的所有方法 --》
《aop:pointcut id=”leeService“
expression=”execution(* lee.*Impl.*(。。))“/》
《!-- 指定在txAdvice切入點(diǎn)應(yīng)用txAdvice事務(wù)切面 --》
《aop:advisor advice-ref=”txAdvice“
pointcut-ref=”leeService“/》
《/aop:config》
《/beans》
提示:
Advisor的作用:將Advice和切入點(diǎn)綁在一起,保證Advice所包含的增強(qiáng)處理在對(duì)應(yīng)的切入點(diǎn)被織入。
主程序:
public class MainTest
{
public static void main(String[] args)
{
//創(chuàng)建Spring容器
ApplicationContext ctx = new
ClassPathXmlApplicationContext(”bean.xml“);
//獲取事務(wù)代理Bean
Test t = (Test)ctx.getBean(”test“);
//執(zhí)行插入操作
t.insert(”bbb“);
}
}
上面的配置文件直接獲取容器中的test Bean,因?yàn)镾pring AOP會(huì)為該Bean自動(dòng)織入事務(wù)增強(qiáng)處理的方式,所以test Bean的所有方法都具有事務(wù)性。
配置《tx:advice.。/》元素時(shí)除了需要一個(gè)transaction-Manager之外,重要的就是需要配置一個(gè)《attributes.。。/》子元素,該元素又可以包含多個(gè)《method.。/》子元素。
可以看出配置《tx:advice.。/》元素重點(diǎn)就是配置《method.。。/》子元素,每個(gè)《method.。/》子元素為一批方法指定所需的事務(wù)語(yǔ)義。
配置《method.。/》元素時(shí)可以指定如下屬性:
name:必選屬性,與該事務(wù)語(yǔ)義關(guān)聯(lián)的方法名,該屬性支持通配符。
propagation:指定事務(wù)傳播行為,該屬性可以為Propagation枚舉類的任意一個(gè)枚舉值,該屬性 的默認(rèn)值是Propagation.REQUIRED;
isolation:指定事務(wù)隔離級(jí)別,該屬性值可為Isolation枚舉類的任意枚舉值。
timeout:指定事務(wù)超時(shí)時(shí)間(以秒為單位),指定-1意味不超時(shí),默認(rèn)值是-1;
read-only:指定事務(wù)是否只讀,該屬性默認(rèn)為false;
rollback-for:指定觸發(fā)事務(wù)回滾的異常類(全名),該屬性可以指定多個(gè)異常類,用英文逗號(hào)隔開;
no-rollback-for:指定不觸發(fā)事務(wù)回滾的類,該屬性可以指定多個(gè)異常類,并且用英文的逗號(hào)隔開。
因此我們可以為不同的業(yè)務(wù)邏輯方法指定不同的事務(wù)策略,如:
《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
①啟動(dòng)Spring容器:
1,直接在web.xml文件中配置Spring容器;
2,利用第三方MVC框架擴(kuò)張點(diǎn),創(chuàng)建Spring容器。
第一種創(chuàng)建Spring容器的方式更加常見,為了讓Spring容器隨web應(yīng)用啟動(dòng)而啟動(dòng),有如下兩種方式:
利用ServletContextListener實(shí)現(xiàn);
采用load-on-startup Servlet實(shí)現(xiàn)。
Spring提供ServletContextListener的一個(gè)實(shí)現(xiàn)類ContextLoadListener,該類可以作為L(zhǎng)istener使用,它會(huì)在創(chuàng)建時(shí)自動(dòng)查找WEB-INF目錄下的ApplicationContext.xml,
如果只有一個(gè)配置文件,并且名字為ApplicationContext.xml,則只需要在web.xml文件添加如下代碼:
《listener》
《listener-class》org.springframework.web.context.ContextLoadListener《/listener-class》
《/listener》
如果需要加載多個(gè)配置文件,則考慮使用《context-param.。/》元素來(lái)確定配置文件的文件名。ContextLoadListener加載時(shí),會(huì)查找名為contextConfigLocation的初始化參數(shù):
《context-param》
《param-name》contextConfigLocation《/param-name》
《param-value》可以配置多個(gè)文件,用逗號(hào)隔開《/param-value》
《/context-param》
除此之外,Spring提供一個(gè)特殊的Servlet類:ContextLoadServlet。該Servlet啟動(dòng)時(shí),也會(huì)自動(dòng)查找WEB-INF路徑下的ApplicationContext.xml文件。為了讓ContextLoadServlet隨應(yīng)用啟動(dòng)而啟動(dòng),應(yīng)該將此Servlet配置成
load-on-startup的值小一點(diǎn)比較合適,這樣可以保證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用于提供后臺(tái)服務(wù)的,主要用于創(chuàng)建Spring容器的,無(wú)需響應(yīng)客戶端請(qǐng)求,因此無(wú)需為它配《servlet-mapping.。/》,如果有多個(gè)配置文件,或者文件名不是applicationContext,則 一樣使用《context-param.。。/》元素來(lái)確定多個(gè)配置文件。
ContextLoadListener和ContextLoadServlet底層都是依賴于ContextLoad。因此二者效果沒(méi)有什么區(qū)別,他們之間的區(qū)別不是它們本身 引起的,而是Servlet規(guī)范:Listener比Servlet優(yōu)先加載,因此采用ContextLoadListener創(chuàng)建ApplicationContext的時(shí)機(jī)更早。
②MVC框架與Spring整合的思考:
對(duì)于一個(gè)基于B/S架構(gòu)的JavaEE而言,用戶請(qǐng)求總是向MVC框架的控制器請(qǐng)求,而當(dāng)控制器攔截到用戶請(qǐng)求后,必須調(diào)用業(yè)務(wù)組件來(lái)處理用戶的請(qǐng)求,那么控制器如何獲取業(yè)務(wù)組件呢?
最容易想到的是,直接通過(guò)new 關(guān)鍵字創(chuàng)建業(yè)務(wù)邏輯,然后調(diào)用該業(yè)務(wù)邏輯組件的方法。
在實(shí)際應(yīng)用中很少采用上面的訪問(wèn)策略,至少有一下幾個(gè)原因:
1:控制器直接創(chuàng)建業(yè)務(wù)組件,導(dǎo)致控制器和業(yè)務(wù)組件的耦合降低到代碼層次,不利于高層次的解耦;
2:控制器不應(yīng)該負(fù)責(zé)業(yè)務(wù)邏輯組件的創(chuàng)建,控制器只是業(yè)務(wù)邏輯的使用者,,無(wú)需關(guān)系業(yè)務(wù)邏輯組件的實(shí)現(xiàn);
3:每次創(chuàng)建新的業(yè)務(wù)組件導(dǎo)致性能降低。
答案是采用工廠模式或者服務(wù)定位器模式,對(duì)于采用服務(wù)定位模式,是遠(yuǎn)程訪問(wèn)的場(chǎng)景,在這種情形下,業(yè)務(wù)組件已經(jīng)在某個(gè)容器中運(yùn)行,并對(duì)外提供某種服務(wù),控制器無(wú)需理會(huì)該業(yè)務(wù)組件的創(chuàng)建,直接調(diào)用該服務(wù)即可,但在調(diào)用該服務(wù)前,必須先找到該服務(wù)。這就是服務(wù)定位模式的概念,經(jīng)典JavaEE應(yīng)用就是這種結(jié)構(gòu)的應(yīng)用。
對(duì)于輕量級(jí)的JavaEE 應(yīng)用,工廠模式則是更實(shí)際的策略,因?yàn)樵谳p量級(jí)JavaEE應(yīng)用中,業(yè)務(wù)組件不是EJB,通常就是一個(gè)POJO,業(yè)務(wù)組件的生成通常應(yīng)由工廠負(fù)責(zé),而且工廠可以保證組件的實(shí)例只需要一個(gè)就夠了,可以避免重復(fù)實(shí)例化造成的系統(tǒng)開銷。
如果系統(tǒng)采用Spring框架,則Spring成為最大的工廠,那么控制器如何訪問(wèn)到Spring容器中的業(yè)務(wù)組件呢?有兩種策略:
1,Spring容器負(fù)責(zé)管理控制器Action,并利用依賴注入為容器注入業(yè)務(wù)組件;
2,利用Spring的自動(dòng)裝配,Action將會(huì)自動(dòng)從Spring容器中獲取所需的業(yè)務(wù)邏輯組件。如:
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è)務(wù)組件:
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“》
《!-- 定義處理用戶請(qǐng)求的Action,該Action的class屬性不是實(shí)際處理類
, 而是Spring容器中的Bean實(shí)例--》
《actionname=”login“class=”loginAction“》
《!-- 為兩個(gè)邏輯視圖配置視圖頁(yè)面--》
《resultname=”error“》/error.jsp《/result》
《resultname=”success“》/success.jsp《/result》
《/action》
《!-- 讓用戶直接訪問(wèn)該應(yīng)用時(shí)列出所有視圖頁(yè)面--》
《actionname=”“》
《result》?!?result》
《/action》
《/package》
《/struts》
7,Spring整合Hibernate
1,Spring提供的dao支持:
DAO模式是標(biāo)準(zhǔn)的java EE設(shè)計(jì)模式,DAO模式的核心思想是所有的數(shù)據(jù)庫(kù)訪問(wèn),都通過(guò)DAO組件完成,DAO組件封裝了數(shù)據(jù)庫(kù)的增刪改查等原子操作,業(yè)務(wù)邏輯組件依賴DAO組件提供的原子操作,
對(duì)于javaEE的應(yīng)用架構(gòu),有非常多的,不管細(xì)節(jié)如何變化,javaEE大致 可以分為如下三層:
表現(xiàn)層;
業(yè)務(wù)邏輯層;
數(shù)據(jù)持久層;
Spring提供了一系列的抽象類,這些抽象類將被作為應(yīng)用中DAO組件可以通過(guò)這些抽象類,Spring簡(jiǎn)化了DAO的開發(fā)步驟,能夠以一致的訪問(wèn)方式使用數(shù)據(jù)庫(kù)訪問(wèn)技術(shù),不管底層采用JDBC、JDO還是個(gè)Hibernate,應(yīng)用中都采用一致的編程模型。
Spring提供的多種數(shù)據(jù)庫(kù)訪問(wèn)技術(shù)的DAO支持,包括Hibernate、JDO、TopLink、iBatis、OJB、JPA等,就Hibernate的持久層訪問(wèn)技術(shù)而言,Spring提供如下3個(gè)工具類來(lái)支持DAO組件的實(shí)現(xiàn):HibernateDaoSupport、HibernateTemplate、HibernateCallback。
2,管理Hibernate的SessionFactory
前面介紹的Hibernate的時(shí)候知道:通過(guò)Hibernate進(jìn)行持久層訪問(wèn)的時(shí)候,Hibernate的SessionFactory是非常重要的對(duì)象,它是單個(gè)數(shù)據(jù)庫(kù)映射關(guān)系編譯后的內(nèi)存鏡像,大部分情況下,一個(gè)java EE應(yīng)用對(duì)應(yīng)一個(gè)數(shù)據(jù)庫(kù),即對(duì)應(yīng)一個(gè)SessionFactory對(duì)象。
配置SessionFactory的示范代碼:
《!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實(shí)現(xiàn) --》
《bean id=”dataSource“ class=”com.mchange.v2.c3p0.ComboPooledDataSource“
destroy-method=”close“》
《!-- 指定連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng) --》
《property name=”driverClass“ value=”com.mysql.jdbc.Driver“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的URL --》
《property name=”jdbcUrl“ value=”jdbc:mysql://localhost/javaee“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的用戶名 --》
《property name=”user“ value=”root“/》
《!-- 指定連接數(shù)據(jù)庫(kù)的密碼 --》
《property name=”password“ value=”32147“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最大連接數(shù) --》
《property name=”maxPoolSize“ value=”40“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的最小連接數(shù) --》
《property name=”minPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的初始化連接數(shù) --》
《property name=”initialPoolSize“ value=”1“/》
《!-- 指定連接數(shù)據(jù)庫(kù)連接池的連接的最大空閑時(shí)間 --》
《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屬性用來(lái)列出全部映射文件 --》
《property name=”mappingResources“》
《list》
《!-- 以下用來(lái)列出Hibernate映射文件 --》
《value》Person.hbm.xml《/value》
《/list》
《/property》
《!-- 定義Hibernate的SessionFactory的屬性 --》
《property name=”hibernateProperties“》
《props》
《!-- 指定數(shù)據(jù)庫(kù)方言 --》
《prop key=”hibernate.dialect“》
org.hibernate.dialect.MySQLInnoDBDialect《/prop》
《!-- 是否根據(jù)需要每次自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù) --》
《prop key=”hibernate.hbm2ddl.auto“》update《/prop》
《!-- 顯示Hibernate持久化操作所生成的SQL --》
《prop key=”hibernate.show_sql“》true《/prop》
《!-- 將SQL腳本進(jìn)行格式化后再輸出 --》
《prop key=”hibernate.format_sql“》true《/prop》
《/props》
《/property》
《/bean》
3,使用HibernateTemplate:
HibernateTemplate提供持久層訪問(wèn)模版化,它只需要提供一個(gè)SessionFactory的引用,SessionFactory可以通過(guò)構(gòu)造方法參數(shù)傳入,也可以通過(guò)設(shè)值方式傳入。
示例:
public class PersonDaoImpl implements PersonDao
{
//定義一個(gè)HibernateTemplate對(duì)象,用于執(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實(shí)例
* @param id 需要加載的Person實(shí)例的標(biāo)識(shí)屬性值
* @return 指定id對(duì)應(yīng)的Person實(shí)例
*/
public Person get(Integer id)
{
return (Person)getHibernateTemplate()
.get(Person.class, id);
}
/**
* 保存Person實(shí)例
* @param person 需要保存的Person實(shí)例
* @return 剛剛保存的Person實(shí)例的標(biāo)識(shí)屬性值
*/
public Integer save(Person person)
{
return (Integer)getHibernateTemplate()
.save(person);
}
/**
* 修改Person實(shí)例
* @param person 需要修改的Person實(shí)例
*/
public void update(Person person)
{
getHibernateTemplate().update(person);
}
/**
* 刪除Person實(shí)例
* @param id 需要?jiǎng)h除的Person實(shí)例的標(biāo)識(shí)屬性值
*/
public void delete(Integer id)
{
getHibernateTemplate().delete(get(id));
}
/**
* 刪除Person實(shí)例
* @param person 需要?jiǎng)h除的Person實(shí)例
*/
public void delete(Person person)
{
getHibernateTemplate().delete(person);
}
/**
* 根據(jù)用戶名查找Person
* @param name 查詢的人名
* @return 指定用戶名對(duì)應(yīng)的全部Person
*/
public List《Person》 findByName(String name)
{
return (List《Person》)getHibernateTemplate()
.find(”from Person p where p.name like ?“ , name);
}
/**
* 查詢?nèi)縋erson實(shí)例
* @return 全部Person實(shí)例
*/
public List findAllPerson()
{
return (List《Person》)getHibernateTemplate()
.find(”from Person“);
}
/**
* 查詢數(shù)據(jù)表中Person實(shí)例的總數(shù)
* @return 數(shù)據(jù)表中Person實(shí)例的總數(shù)
*/
public long getPersonNumber()
{
return (Long)getHibernateTemplate().find
?。ā眘elect count(*) from Person as p“)
.get(0);
}
}
4,使用HibernateCallback:
HibernateTemplate還提供一種更加靈活的方式來(lái)操作數(shù)據(jù)庫(kù),,通過(guò)這種方式可以完全使用Hibernate的操作方式,HibernateTemplate的靈活方式是通過(guò)如下兩個(gè)方法來(lái)完成的。
Object execute(HibernateCallback action);
List executeFind(HibernateCallback action);
這兩個(gè)方法都需要一個(gè)HibernateCallback實(shí)例,HibernateCallback是一個(gè)接口,該接口包含一個(gè)方法doInHibernate(org.hibernate Session session),該方法是有一個(gè)Session參數(shù),當(dāng)我們?cè)陂_發(fā)中提供HibernateCallback實(shí)現(xiàn)類時(shí),必須實(shí)現(xiàn)接口里的doInHibernate方法,該方法體內(nèi)即可獲得 Hibernate Session的引用。一旦獲取到Session的引用我們就可以完全以Hibernate的方式進(jìn)行數(shù)據(jù)庫(kù)的訪問(wèn)。
示例:
public class YeekuHibernateDaoSupport extends HibernateDaoSupport
{
/**
* 使用hql語(yǔ)句進(jìn)行分頁(yè)查詢
* @param hql 需要查詢的hql語(yǔ)句
* @param offset 第一條記錄索引
* @param pageSize 每頁(yè)需要顯示的記錄數(shù)
* @return 當(dāng)前頁(yè)的所有記錄
*/
public List findByPage(final String hql,
final int offset, final int pageSize)
{
//通過(guò)一個(gè)HibernateCallback對(duì)象來(lái)執(zhí)行查詢
List list = getHibernateTemplate()
.executeFind(new HibernateCallback()
{
//實(shí)現(xiàn)HibernateCallback接口必須實(shí)現(xiàn)的方法
public Object doInHibernate(Session session)
throws HibernateException, SQLException
{
//執(zhí)行Hibernate分頁(yè)查詢
List result = session.createQuery(hql)
.setFirstResult(offset)
.setMaxResults(pageSize)
.list();
return result;
}
});
return list;
}
/**
* 使用hql語(yǔ)句進(jìn)行分頁(yè)查詢
* @param hql 需要查詢的hql語(yǔ)句
* @param value 如果hql有一個(gè)參數(shù)需要傳入,value就是傳入hql語(yǔ)句的參數(shù)
* @param offset 第一條記錄索引
* @param pageSize 每頁(yè)需要顯示的記錄數(shù)
* @return 當(dāng)前頁(yè)的所有記錄
*/
public List findByPage(final String hql , final Object value ,
final int offset, final int pageSize)
{
//通過(guò)一個(gè)HibernateCallback對(duì)象來(lái)執(zhí)行查詢
List list = getHibernateTemplate()
.executeFind(new HibernateCallback()
{
//實(shí)現(xiàn)HibernateCallback接口必須實(shí)現(xiàn)的方法
public Object doInHibernate(Session session)
throws HibernateException, SQLException
{
//執(zhí)行Hibernate分頁(yè)查詢
List result = session.createQuery(hql)
//為hql語(yǔ)句傳入?yún)?shù)
.setParameter(0, value)
.setFirstResult(offset)
.setMaxResults(pageSize)
.list();
return result;
}
});
return list;
}
/**
* 使用hql語(yǔ)句進(jìn)行分頁(yè)查詢
* @param hql 需要查詢的hql語(yǔ)句
* @param values 如果hql有多個(gè)個(gè)參數(shù)需要傳入,values就是傳入hql的參數(shù)數(shù)組
* @param offset 第一條記錄索引
* @param pageSize 每頁(yè)需要顯示的記錄數(shù)
* @return 當(dāng)前頁(yè)的所有記錄
*/
public List findByPage(final String hql, final Object[] values,
final int offset, final int pageSize)
{
//通過(guò)一個(gè)HibernateCallback對(duì)象來(lái)執(zhí)行查詢
List list = getHibernateTemplate()
.executeFind(new HibernateCallback()
{
//實(shí)現(xiàn)HibernateCallback接口必須實(shí)現(xiàn)的方法
public Object doInHibernate(Session session)
throws HibernateException, SQLException
{
//執(zhí)行Hibernate分頁(yè)查詢
Query query = session.createQuery(hql);
//為hql語(yǔ)句傳入?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互為補(bǔ)充,XxxTemplate對(duì)操作進(jìn)行封裝,XxxCallback解決了封裝后靈活性不足的缺陷。
5,實(shí)現(xiàn)DAO組件:
為了實(shí)現(xiàn)DAO組件,Spring提供了大量的XxxDaoSupport類,這些dao支持類已經(jīng)完成了大量基礎(chǔ)性的工作。
Spring為Hibernate的DAO提供工具類:HibernateDaoSupport,該類主要提供如下幾個(gè)方法來(lái)簡(jiǎn)化DAO的實(shí)現(xiàn):
public final HibernateTemplate getHibernateTemplate ();
public final void setSessionFactory(SessionFactory sessionFactory);
setSessionFactory方法可用于接收Spring的依賴注入,允許使用依賴注入Spring 管理的SessionFactory實(shí)例。
public class PersonDaoHibernate
extends HibernateDaoSupport implements PersonDao
{
/**
* 加載Person實(shí)例
* @param id 需要加載的Person實(shí)例的標(biāo)識(shí)屬性值
* @return 指定id對(duì)應(yīng)的Person實(shí)例
*/
public Person get(Integer id)
{
return (Person)getHibernateTemplate()
.get(Person.class, id);
}
/**
* 保存Person實(shí)例
* @param person 需要保存的Person實(shí)例
* @return 剛剛保存的Person實(shí)例的標(biāo)識(shí)屬性值
*/
public Integer save(Person person)
{
return (Integer)getHibernateTemplate()
.save(person);
}
/**
* 修改Person實(shí)例
* @param person 需要修改的Person實(shí)例
*/
public void update(Person person)
{
getHibernateTemplate().update(person);
}
/**
* 刪除Person實(shí)例
* @param id 需要?jiǎng)h除的Person實(shí)例的標(biāo)識(shí)屬性值
*/
public void delete(Integer id)
{
getHibernateTemplate().delete(get(id));
}
/**
* 刪除Person實(shí)例
* @param person 需要?jiǎng)h除的Person實(shí)例
*/
public void delete(Person person)
{
getHibernateTemplate().delete(person);
}
/**
* 根據(jù)用戶名查找Person
* @param name 查詢的人名
* @return 指定用戶名對(duì)應(yīng)的全部Person
*/
public List《Person》 findByName(String name)
{
return (List《Person》)getHibernateTemplate()
.find(”from Person p where p.name like ?“ , name);
}
/**
* 查詢?nèi)縋erson實(shí)例
* @return 全部Person實(shí)例
*/
public List findAllPerson()
{
return (List《Person》)getHibernateTemplate()
.find(”from Person“);
}
}
實(shí)際上,DAO的實(shí)現(xiàn)依然借助于HibernateTemplate的模板訪問(wèn)方式,只是HibernateDaoSupport提供了兩個(gè)工具方法。
至此Java EE應(yīng)用所需的各種組件都已經(jīng)出現(xiàn)了。
從用戶的角度上看,用戶發(fā)出HTTP請(qǐng)求,當(dāng)MVC框架的控制器組件攔截到用戶的請(qǐng)求,將調(diào)用系統(tǒng)的業(yè)務(wù)邏輯組件,業(yè)務(wù)邏輯組件則調(diào)用 系統(tǒng)的DAO組件,,而DAO組件則依賴于SessionFactory和DataSOurce等底層的實(shí)現(xiàn)數(shù)據(jù)庫(kù)訪問(wèn)。
從系統(tǒng)的實(shí)現(xiàn)角度上看,IoC容器先創(chuàng)建SessionFactory和DataSource等底層組件,然后將這些組件注入DAO組件,提供一個(gè)完整的Dao組件,并將DAO組件注入給業(yè)務(wù)邏輯組件,從而提供一個(gè)完整的業(yè)務(wù)邏輯組件,而業(yè)務(wù)邏輯組件又被注入給控制器組件,控制器組件負(fù)責(zé)攔截用戶請(qǐng)求,并將處理結(jié)果呈現(xiàn)給用戶。
評(píng)論