對(duì)Spring AOP框架實(shí)現(xiàn)的結(jié)構(gòu)分析
本文的目標(biāo):
從實(shí)現(xiàn)的角度來認(rèn)識(shí)SpringAOP框架。
觀察的角度:
從外部接口,內(nèi)部實(shí)現(xiàn),組成部分,執(zhí)行過程四個(gè)方面來認(rèn)識(shí)SpringAOP框架。
本文的風(fēng)格:
首先列出AOP的基本概念;
其次介紹框架所涉及到的核心組件列表,組件之間的結(jié)構(gòu)關(guān)系圖;
然后細(xì)化結(jié)構(gòu)圖中的部分;
接下來是一個(gè)簡單的sample;
***是后記部分。
注:
1.本文的源代碼基于Spring2.x。Spring的源代碼也處于演變中,但對(duì)基礎(chǔ)代碼的影響并不大。
2.本文是對(duì)Spring IoC容器實(shí)現(xiàn)的結(jié)構(gòu)分析的姊妹帖。
正文:
Spring AOP框架涉及的基本概念介紹:
關(guān)注點(diǎn)(concern):一個(gè)關(guān)注點(diǎn)可以是一個(gè)特定的問題、概念、或是應(yīng)用程序的興趣區(qū)間--總而言之,應(yīng)用程序必須達(dá)到的一個(gè)目標(biāo)。
核心關(guān)注點(diǎn)(core concern):業(yè)務(wù)功能模塊,如:存款模塊,取款模塊,轉(zhuǎn)賬模塊等,
橫切關(guān)注點(diǎn)(crosscutting concern):非功能性的、橫切性模塊,如:安全性管理,事務(wù)管理,性能監(jiān)控等。
方面(aspect):一個(gè)方面是對(duì)一個(gè)橫切關(guān)注點(diǎn)的模塊化,它將那些原本散落在各處的、用于實(shí)現(xiàn)這個(gè)關(guān)注點(diǎn)的代碼歸整到一處。
連接點(diǎn)(join point):程序執(zhí)行過程中的一點(diǎn),如:
字段訪問:讀、寫實(shí)例變量;
方法調(diào)用:對(duì)方法(包括構(gòu)造方法)的調(diào)用;
異常拋出:特定的異常被拋出。
切入點(diǎn)(pointcut):一組連接點(diǎn)的總稱,用于指定某個(gè)增強(qiáng)應(yīng)該在何時(shí)被調(diào)用。切入點(diǎn)常用正則表達(dá)式或別的通配符語法來描述,有些AOP實(shí)現(xiàn)技術(shù)還支持切入點(diǎn)的組合。
增強(qiáng)(advice):在特定連接點(diǎn)執(zhí)行的動(dòng)作。很多AOP框架都以攔截器(interceptor)的形式來表現(xiàn)增強(qiáng)--所謂攔截器是這樣的一個(gè)
對(duì)象:當(dāng)連接點(diǎn)被調(diào)用時(shí),它會(huì)收到一個(gè)回調(diào)消息?;镜脑鰪?qiáng)有:
前增強(qiáng)(BeforeAdvice):在連接點(diǎn)調(diào)用之前,首先調(diào)用增強(qiáng);
后增強(qiáng)(AfterAdvice):在連接點(diǎn)調(diào)用之后,再調(diào)用增強(qiáng),在AspectJ中,后增強(qiáng)又分為三種:
AfterReturningAdvice:在調(diào)用成功完成(沒有異常拋出)之后。
AfterThrowingAdvice:在拋出某種特定類型(或其子類型)的異常之后。
AfterAdvice:在連接點(diǎn)的任何調(diào)用之后,不管調(diào)用是否拋出異常。
環(huán)繞增強(qiáng)(AroundAdvice):這類增強(qiáng)可以完全控制執(zhí)行流程。除了完成本身的工作之外,它還需要負(fù)責(zé)主動(dòng)調(diào)用連接點(diǎn),促使真實(shí)的操作發(fā)生(proceed)-- 這通常是通過調(diào)用某個(gè)特定的方法來完成的。
引介(introduction):為一個(gè)現(xiàn)有的Java類或接口添加方法或字段。這種技術(shù)可以用于實(shí)現(xiàn)Java中的多繼承,或者給現(xiàn)有對(duì)象模型附加新的API。
混入繼承(mixin inheritance):一個(gè)“混入類”封裝了一組功能,這組功能可以被"混入"到現(xiàn)有的類當(dāng)中,并且無須使用傳統(tǒng)的繼承手段。在AOP這里,混入是通過引介來實(shí)現(xiàn)的。在Java語言中,可以通過混入來模擬多繼承。
織入(weaving):將方面整合到完整的執(zhí)行流程(或完整的類,此時(shí)被織入的便是引介中)。
攔截器(initerceptor):很多AOP框架用它來實(shí)現(xiàn)字段和方法的攔截(interception)。隨之而來的就是在連接點(diǎn)(如方法攔截)處掛接一條攔截器鏈(interceptor chain),鏈條上的每個(gè)攔截器通常會(huì)調(diào)用下一個(gè)攔截器。
AOP代理(AOP proxy):即被增強(qiáng)(advise)的對(duì)象引用--也就是說,AOP增強(qiáng)將在其上執(zhí)行的這樣一個(gè)對(duì)象引用。
目標(biāo)對(duì)象(target object):位于攔截器鏈末端的對(duì)象實(shí)例--這個(gè)概念只存在于那些使用了攔截機(jī)制的框架之中。
注:上述概念描述引自《Expert One-on-One J2EE Development without EJB》中第八章對(duì)AOP概念描述部分,更多精彩部分可以參閱本章的完整內(nèi)容。
上述概念已被Spring AOP框架很好的實(shí)現(xiàn),相關(guān)組件:
Advisor 組件,
Advice 組件,
Pointcut 組件,
Advised 組件,
AopProxy 組件,
AopProxyFactory 組件,
圖1.

圖1是對(duì)增強(qiáng)、切入點(diǎn)、方面、AOP代理之間依賴關(guān)系的全景圖。
增強(qiáng)和切入點(diǎn)組成一個(gè)方面,方面信息與目標(biāo)對(duì)象信息被組織到Advised中,AopProxyFactory通過Advised中保存的信息生成AopProxy
對(duì)象,調(diào)用AopProxy.getProxy()方法即可獲得增強(qiáng)后的對(duì)象。
這里要著重了解的是不同的增強(qiáng)子類型,不同的切入點(diǎn)子類型,
對(duì)于不同的切入點(diǎn)子類型最重要的兩種子類型:靜態(tài)切入點(diǎn),動(dòng)態(tài)切入點(diǎn),
靜態(tài)切入點(diǎn):根據(jù)部署階段的信息選擇增強(qiáng),如“攔截特定類的所有g(shù)etter方法”;
動(dòng)態(tài)切入點(diǎn):根據(jù)運(yùn)行時(shí)的信息選擇增強(qiáng),如“如果某方法的返回值為null,則將其納入某切入點(diǎn)”。
圖2.

圖2是對(duì)圖1中Advisor與Pointcut的實(shí)現(xiàn)細(xì)化,圖中類之間的關(guān)系直觀上有點(diǎn)亂,但細(xì)看下關(guān)系還是相當(dāng)清晰的,
以Advisor結(jié)尾的是方面類型,以Pointcut結(jié)尾的是切入點(diǎn)類型,
Advisor與Pointcut的復(fù)用關(guān)系分兩類:一類是組合復(fù)用,另一類是具體繼承復(fù)用,
組合復(fù)用例子 如:RegexpMethodPointcutAdvisor 與 AbstractRegexpMethodPointcut之間的關(guān)系,
NameMatchMethodPointcutAdvisor 與 NameMatchMethodPointcut之間的關(guān)系,
具體繼承復(fù)用例子 如:StaticMethodMatcherPointcutAdvisor 與 StaticMethodMatcherPointcut 之間的關(guān)系,
DynamicMethodMatcherPointcutAdvisor 與 DynamicMethodMatcherPointcut 之間的關(guān)系,
圖3.

圖3是對(duì)圖1中生成AopProxy對(duì)象的實(shí)現(xiàn)細(xì)化,
AopProxyFactory通過AdvisedSupport提供的信息生成AopProxy對(duì)象,AopProxy對(duì)象的生成分兩類方式:一類是動(dòng)態(tài)代理,另一類是字節(jié)碼增強(qiáng);
需要注意的是,ProxyFactory與ProxyFactoryBean并不是功能實(shí)現(xiàn)的必要部分,主要目的為編程式使用代理提供便利的API。
#p#
下面是一個(gè)簡單的sample:
- //目標(biāo)對(duì)象接口.
- public interface Target {
- public String play(int arg);
- }
- //目標(biāo)對(duì)象實(shí)現(xiàn).
- public class TargetImpl implements Target {
- public String play(int arg) {
- System.out.println("play method....");
- return "[Target:]" + arg;
- }
- }
- //前置增強(qiáng)
- public class MyBeforeAdvice implements MethodBeforeAdvice {
- public void before(Method method, Object[] args, Object target)
- throws Throwable {
- System.out.println(method.getName());
- System.out.println("before method!");
- }
- }
- //后置增強(qiáng)
- public class MyAfterAdvice implements AfterReturningAdvice {
- public void afterReturning(Object returnValue, Method method,
- Object[] args, Object target) throws Throwable {
- System.out.println(returnValue + ":after method");
- }
- }
- //切入點(diǎn)實(shí)現(xiàn)
- public class MyPointcut implements Pointcut {
- public ClassFilter getClassFilter() {
- return new ClassFilter() {
- public boolean matches(Class arg0) {
- if (arg0 == TargetImpl.class) {
- return true;
- }
- return false;
- }
- };
- }
- public MethodMatcher getMethodMatcher() {
- return new MethodMatcher() {
- public boolean isRuntime() {
- return false;
- }
- public boolean matches(Method arg0, Class arg1) {
- if ("play".equals(arg0.getName())) {
- return true;
- }
- return false;
- }
- public boolean matches(Method arg0, Class arg1, Object[] arg2) {
- System.out.println("aaaaaa");
- if ("play".equals(arg0.getName())) {
- return true;
- }
- return false;
- }
- };
- }
- }
- public class Main {
- public static void main(String[] args) {
- Target target = new TargetImpl(); //目標(biāo)對(duì)象
- Advice beforeAdvice = new MyBeforeAdvice(); //增強(qiáng)
- Pointcut pointcut = new MyPointcut(); //切入點(diǎn)
- DefaultPointcutAdvisor dda = new DefaultPointcutAdvisor(); //切面
- dda.setAdvice(beforeAdvice);
- dda.setPointcut(pointcut);
- AdvisedSupport advisedSupport = new AdvisedSupport(); //提供基本的編程方式,
- advisedSupport.addAdvisor(dda);
- advisedSupport.addAdvice(new MyAfterAdvice());
- advisedSupport.addInterface(Target.class);
- advisedSupport.setTarget(target);
- AopProxy aopProxy = new DefaultAopProxyFactory().createAopProxy(advisedSupport);
- Target proxy = (Target)aopProxy.getProxy();
- System.out.println(proxy.play(200));
- ProxyFactory proxyFactory = new ProxyFactory(); //提供便利的編程方式.
- proxyFactory.addAdvisor(dda);
- proxyFactory.addInterface(Target.class);
- proxyFactory.setTarget(target);
- Target proxy2 = (Target)proxyFactory.getProxy();
- System.out.println(proxy2.play(201));
- ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); //提供便利的配置方式
- proxyFactoryBean.addAdvisor(dda);
- proxyFactoryBean.addInterface(Target.class);
- proxyFactoryBean.setTarget(target);
- Target proxy3 = (Target)proxyFactoryBean.getObject();
- System.out.println(proxy3.play(232));
- }
- }
注:此處為了簡單忽略了一些概念描述:
1.對(duì)引介,混入繼承沒有涉及。
2.對(duì)通知鏈的管理,如:同一切入點(diǎn)多個(gè)通知對(duì)象之間的執(zhí)行順序問題;
3.上述的描述是Spring AOP框架本身,所提供的使用方式是編程式,這種使用方式太過于低級(jí),以至于我們?cè)诰懦傻那闆r下不會(huì)使用到,主流的使用方式是配置化的聲明式使用方式,將AOP與IoC結(jié)合起來使用才能發(fā)揮出***威力,ProxyFactoryBean提供了有限的聲明式使用方式,但嚴(yán)格來說它仍是編程式,因?yàn)镻roxyFactoryBean是一個(gè)FactoryBean,一個(gè)FactoryBean的目標(biāo)就是以編程式替換復(fù)雜的配置式,而且最重要的是它暴露的API太過低級(jí),配置文件中bean元素的abstract屬性對(duì)配置文件的長度提供有限的幫助,自動(dòng)代DefaultAdvisorAutoProxyCreator很好的隱藏了低級(jí)的API,DefaultAdvisorAutoProxyCreator是一個(gè)BeanPostProcessor,用于完成AOP框架與IoC容器的集成工作,但是這種方式依然沒有解決需要同XXXAdvisor這樣的低級(jí)API打交道的問題;
隨著spring2.x引入的xml元素
原文:http://www.iteye.com/topic/1114645
使得我們只需關(guān)注高層描述,而無需涉及低級(jí)API。
附上DefaultAdvisorAutoProxyCreator的類結(jié)構(gòu)圖:

總結(jié):
要全面理解AOP相關(guān)概念,回答下述問題是必須的。
1。AOP概念產(chǎn)生的背景,AOP所要解決的問題,AOP所涉及的概念,
2。實(shí)現(xiàn)一個(gè)AOP框架所需要注意的問題是什么,
3。不同AOP框架實(shí)現(xiàn)之間的比較,
4。AOP的一些副作用討論。