?
This document uses PHP Chinese website manual Release
到目前為止我們已經(jīng)考慮了如何使用ProxyFactoryBean
或者類似的工廠bean來顯式創(chuàng)建AOP代理。
Spring也允許我們使用“自動(dòng)代理”的bean定義,可以自動(dòng)對(duì)被選中的bean定義進(jìn)行代理。 這建立在Spring的“bean post processor”功能上,后者允許在容器加載時(shí)修改任何bean的定義。
在這個(gè)模型下,你在你的XML bean定義文件中建立一些特定的bean定義來配置自動(dòng)代理功能。
這允許你僅僅聲明那些將被自動(dòng)代理的適當(dāng)目標(biāo):你不需要使用ProxyFactoryBean
。
有兩種方式可以做到這點(diǎn):
使用一個(gè)引用當(dāng)前上下文中特定bean的自動(dòng)代理創(chuàng)建器。
一個(gè)專用自動(dòng)代理的創(chuàng)建需要被單獨(dú)考慮;自動(dòng)代理創(chuàng)建由源代碼級(jí)別的元數(shù)據(jù)屬性驅(qū)動(dòng)。
org.springframework.aop.framework.autoproxy
包提供了下列標(biāo)準(zhǔn)自動(dòng)代理創(chuàng)建器。
BeanNameAutoProxyCreator為名字匹配字符串或者通配符的bean自動(dòng)創(chuàng)建AOP代理。
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"><value>jdk*,onlyJdk</value></property> <property name="interceptorNames"> <list> <value>myInterceptor</value> </list> </property> </bean>
和ProxyFactoryBean
一樣,這里有一個(gè)interceptorNames
屬性而不是一個(gè)攔截器的列表,這允許使用原型(prototype)通知器。這里的“攔截器”可以是通知器或任何通知類型。
與通常的自動(dòng)代理一樣,使用BeanNameAutoProxyCreator
的主要目的是把相同的配置一致地應(yīng)用到多個(gè)對(duì)象,
并且使用最少量的配置。一個(gè)流行的選擇是把聲明式事務(wù)應(yīng)用到多個(gè)對(duì)象上。
那些名字匹配的Bean定義,例如上面的例子里的“jdkMyBean”和“onlyJdk”,本身只是目標(biāo)類的普通bean定義。一個(gè)AOP對(duì)象將被BeanNameAutoProxyCreator
自動(dòng)創(chuàng)建。
相同的通知將被應(yīng)用到全部匹配的bean上。注意如果通知器被使用(而不是像上面例子里那樣使用攔截器),對(duì)于不同bean可以應(yīng)用不同的切入點(diǎn)。
一個(gè)更加通用而且強(qiáng)大得多的自動(dòng)代理創(chuàng)建器是DefaultAdvisorAutoProxyCreator
。它自動(dòng)應(yīng)用當(dāng)前上下文中適當(dāng)?shù)耐ㄖ?,無需在自動(dòng)代理通知器的bean定義中包括bean的名字。
比起BeanNameAutoProxyCreator
,它提供了同樣關(guān)于一致性配置的優(yōu)點(diǎn)而避免了前者的重復(fù)性。
使用這個(gè)功能將涉及:
Specifying a DefaultAdvisorAutoProxyCreator
bean
definition.
說明一個(gè)
DefaultAdvisorAutoProxyCreator
的bean定義
在同一個(gè)或者相關(guān)的上下文中說明任意數(shù)量的通知器。注意這些必須是通知器而不僅僅是攔截器或者其它通知。 這點(diǎn)是必要的因?yàn)楸仨氂幸粋€(gè)切入點(diǎn)被評(píng)估,以便檢查每個(gè)通知候選bean定義的合適性。
DefaultAdvisorAutoProxyCreator
將自動(dòng)評(píng)估包括在每個(gè)通知器中的切入點(diǎn),
來看看它應(yīng)當(dāng)應(yīng)用哪個(gè)(如果有的話)通知到每個(gè)業(yè)務(wù)對(duì)象(例如例子里的“businessObject1”和“businessObject2”)。
這意味著可以向每個(gè)業(yè)務(wù)對(duì)象應(yīng)用任意數(shù)量的通知器。 對(duì)于一個(gè)業(yè)務(wù)對(duì)象,如果所有通知器中的切入點(diǎn)都無法匹配它的任意方法,這個(gè)對(duì)象將不會(huì)被代理。當(dāng)為新的業(yè)務(wù)對(duì)象加入bean定義時(shí),如果有必要它們將自動(dòng)被代理。
通常自動(dòng)代理的好處是它讓調(diào)用者或者被依賴對(duì)象不能得到一個(gè)沒有通知過的對(duì)象。 在這個(gè)ApplicationContext上調(diào)用getBean("businessObject1")將返回一個(gè)AOP代理,而不是目標(biāo)業(yè)務(wù)對(duì)象。(前面顯示的“內(nèi)部bean”也提供了同樣的優(yōu)點(diǎn)。)
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"> <property name="transactionInterceptor" ref="transactionInterceptor"/> </bean> <bean id="customAdvisor" class="com.mycompany.MyAdvisor"/> <bean id="businessObject1" class="com.mycompany.BusinessObject1"> <!-- Properties omitted --> </bean> <bean id="businessObject2" class="com.mycompany.BusinessObject2"/>
如果你想要把相同的通知一致性地應(yīng)用到許多業(yè)務(wù)對(duì)象上,DefaultAdvisorAutoProxyCreator
是非常有用的。
一旦框架的定義已經(jīng)完成,你可以簡單地加入新的業(yè)務(wù)對(duì)象而不必包括特定的代理配置。你也可以很容易的去掉額外的切面--例如,跟蹤或者性能監(jiān)視切面--僅僅對(duì)配置作很小的修改。
DefaultAdvisorAutoProxyCreator支持過濾(通過使用一個(gè)命名約定讓只有特定的通知器被評(píng)估,允許在同一個(gè)工廠里使用多個(gè)不同配置的AdvisorAutoProxyCreator)和排序。通知器可以實(shí)現(xiàn)org.springframework.core.Ordered
接口來確保以正確的順序被應(yīng)用。
上面例子里的TransactionAttributeSourceAdvisor 有一個(gè)可配置的序號(hào)值;缺省情況下是沒有排序的。
一個(gè)非常重要的自動(dòng)代理類型是由元數(shù)據(jù)驅(qū)動(dòng)的。這提供了一種和.NET ServicedComponents
相似的編程模型。
作為使用類似EJB里的XML描述符的替代,對(duì)于事務(wù)管理和其它企業(yè)服務(wù)的配置都將被保存在源代碼級(jí)別的屬性里。
在這個(gè)情況下,你使用DefaultAdvisorAutoProxyCreator
和可以理解元數(shù)據(jù)屬性的通知器。
元數(shù)據(jù)被保存在候選通知器的切入點(diǎn)部分中,而不是在自動(dòng)代理創(chuàng)建類本身。
這是一個(gè)DefaultAdvisorAutoProxyCreator
的特殊例子,它本身沒有什么特別。(元數(shù)據(jù)的相關(guān)代碼保存在通知器的切入點(diǎn)里,而不是AOP框架里)。
JPetStore示例應(yīng)用程序的/attributes
目錄顯示了如何使用參數(shù)驅(qū)動(dòng)的自動(dòng)代理。在這個(gè)例子里,不需要使用TransactionProxyFactoryBean
。因?yàn)槭褂昧嗽獢?shù)據(jù)相關(guān)的切入點(diǎn),
所以簡單在業(yè)務(wù)對(duì)象上定義事務(wù)屬性就足夠了。在/WEB-INF/declarativeServices.xml
里的bean定義包括了下面的片斷,注意這是通用的,可以被用在JPetStore以外的地方:
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"> <property name="transactionInterceptor" ref="transactionInterceptor"/> </bean> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager"/> <property name="transactionAttributeSource"> <bean class="org.springframework.transaction.interceptor.AttributesTransactionAttributeSource"> <property name="attributes" ref="attributes"/> </bean> </property> </bean> <bean id="attributes" class="org.springframework.metadata.commons.CommonsAttributes"/>
DefaultAdvisorAutoProxyCreator
bean定義(名字是不重要的,因此甚至可以在定義里省略它)將在當(dāng)前應(yīng)用程序上下文中查找所有合適的切入點(diǎn)。在這個(gè)例子里,TransactionAttributeSourceAdvisor
類型的“transactionAdvisor”bean定義將應(yīng)用到帶有一個(gè)事務(wù)屬性的類或方法上。
TransactionAttributeSourceAdvisor的構(gòu)造器依賴于一個(gè)TransactionInterceptor。這個(gè)例子里通過自動(dòng)織入解決了這個(gè)問題。AttributesTransactionAttributeSource
依賴于一個(gè)org.springframework.metadata.Attributes
接口的實(shí)現(xiàn)。
在這個(gè)代碼片斷里,“attributes”bean使用Jakarta Commons Attributes API來獲取屬性信息以滿足這個(gè)要求。(應(yīng)用程序代碼必須已經(jīng)使用Commons Attribut來es的編譯任務(wù)編譯過了。)
JPetStore示例應(yīng)用程序的 /annotation
目錄包括了一個(gè)由JDK 1.5+注解驅(qū)動(dòng)的自動(dòng)代理的模擬例子。
下面的配置允許自動(dòng)檢測(cè)Spring的Transactional
注解,這可以為包含注解的bean提供隱式代理:
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor"> <property name="transactionInterceptor" ref="transactionInterceptor"/> </bean> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager"/> <property name="transactionAttributeSource"> <bean class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/> </property> </bean>
這里定義的TransactionInterceptor
依賴于一個(gè)PlatformTransactionManager
定義,
后者沒有被包括在這個(gè)通用的文件里(雖然它可以被包括在這里)因?yàn)樗趹?yīng)用程序的事務(wù)需求規(guī)范中指定(在這個(gè)例子里使用JTA,而在其它情況下,可以是Hibernate,JDO或者JDBC):
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
如果你只需要聲明式事務(wù)管理,使用這些通用的XML定義將導(dǎo)致Spring自動(dòng)代理所有帶有事務(wù)屬性的類或者方法。你將不需要直接使用AOP工作, 這個(gè)編程模型和.NET的ServicedComponents相似。
這個(gè)架構(gòu)是可以擴(kuò)展的??梢栽谧远x屬性的基礎(chǔ)上進(jìn)行自動(dòng)代理。你所需要做的是:
定義你自己的自定義屬性
使用必要的通知說明一個(gè)通知器,也包括一個(gè)切入點(diǎn),后者可以被類或者方法上的自定義屬性觸發(fā)。 你也許能夠使用已有的通知,而僅僅實(shí)現(xiàn)一個(gè)能夠處理自定義屬性的靜態(tài)切入點(diǎn)。
可以讓這些通知器對(duì)于每個(gè)被通知對(duì)象(例如,mixins)都是唯一的:僅僅需要在bean定義中被定義為原型而不是單例。
例如,在上面所顯示的Spring測(cè)試集中的LockMixin
引入攔截器可以和一個(gè)屬性驅(qū)動(dòng)的切入點(diǎn)聯(lián)合定位一個(gè)mixin,
像這里顯示的這樣。我們使用通用的DefaultPointcutAdvisor
,使用JavaBean屬性進(jìn)行配置:
<bean id="lockMixin" class="org.springframework.aop.LockMixin" scope="prototype"/> <bean id="lockableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor" scope="prototype"> <property name="pointcut" ref="myAttributeAwarePointcut"/> <property name="advice" ref="lockMixin"/> </bean> <bean id="anyBean" class="anyclass" ...
如果參數(shù)相關(guān)的切入點(diǎn)匹配anyBean
或其它bean定義里的任何方法,mixin將被應(yīng)用。
注意lockMixin
和lockableAdvisor
的定義都是原型。
myAttributeAwarePointcut
切入點(diǎn)可以是個(gè)單例,因?yàn)樗鼪]有為單個(gè)被通知對(duì)象保持狀態(tài)。