方面(Aspect):一個(gè)關(guān)注點(diǎn)的模塊化,這個(gè)關(guān)注點(diǎn)實(shí)現(xiàn)可能另外橫切多個(gè)對(duì)象。事務(wù)管理是J2EE應(yīng)用中一個(gè)很好的橫切關(guān)注點(diǎn)例子。方面用spring的 Advisor或攔截器實(shí)現(xiàn)。
連接點(diǎn)(Joinpoint): 程序執(zhí)行過(guò)程中明確的點(diǎn),如方法的調(diào)用或特定的異常被拋出。
通知(Advice): 在特定的連接點(diǎn),AOP框架執(zhí)行的動(dòng)作。各種類(lèi)型的通知包括“around”、“before”和“throws”通知。通知類(lèi)型將在下面討論。許多AOP框架包括Spring都是以攔截器做通知模型,維護(hù)一個(gè)“圍繞”連接點(diǎn)的攔截器鏈。Spring中定義了四個(gè)advice: BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice
切入點(diǎn)(Pointcut): 指定一個(gè)通知將被引發(fā)的一系列連接點(diǎn)的集合。AOP框架必須允許開(kāi)發(fā)者指定切入點(diǎn):例如,使用正則表達(dá)式。 Spring定義了Pointcut接口,用來(lái)組合MethodMatcher和ClassFilter,可以通過(guò)名字很清楚的理解, MethodMatcher是用來(lái)檢查目標(biāo)類(lèi)的方法是否可以被應(yīng)用此通知,而ClassFilter是用來(lái)檢查Pointcut是否應(yīng)該應(yīng)用到目標(biāo)類(lèi)上
引入(Introduction): 添加方法或字段到被通知的類(lèi)。 Spring允許引入新的接口到任何被通知的對(duì)象。例如,你可以使用一個(gè)引入使任何對(duì)象實(shí)現(xiàn) IsModified接口,來(lái)簡(jiǎn)化緩存。Spring中要使用Introduction, 可有通過(guò)DelegatingIntroductionInterceptor來(lái)實(shí)現(xiàn)通知,通過(guò)DefaultIntroductionAdvisor來(lái)配置Advice和代理類(lèi)要實(shí)現(xiàn)的接口
目標(biāo)對(duì)象(Target Object): 包含連接點(diǎn)的對(duì)象。也被稱(chēng)作被通知或被代理對(duì)象。POJO
AOP代理(AOP Proxy): AOP框架創(chuàng)建的對(duì)象,包含通知。 在Spring中,AOP代理可以是JDK動(dòng)態(tài)代理或者CGLIB代理。
織入(Weaving): 組裝方面來(lái)創(chuàng)建一個(gè)被通知對(duì)象。這可以在編譯時(shí)完成(例如使用AspectJ編譯器),也可以在運(yùn)行時(shí)完成。Spring和其他純Java?AOP框架一樣,在運(yùn)行時(shí)完成織入。
什么是AOP
AOP(Aspect-OrientedProgramming,面向方面編程),可以說(shuō)是OOP(Object-Oriented Programing,面向?qū)ο缶幊蹋┑难a(bǔ)充和完善。OOP引入封裝、繼承和多態(tài)性等概念來(lái)建立一種對(duì)象層次結(jié)構(gòu),用以模擬公共行為的一個(gè)集合。當(dāng)我們需要為分散的對(duì)象引入公共行為的時(shí)候,OOP則顯得無(wú)能為力。也就是說(shuō),OOP允許你定義從上到下的關(guān)系,但并不適合定義從左到右的關(guān)系。例如日志功能。日志代碼往往水平地散布在所有對(duì)象層次中,而與它所散布到的對(duì)象的核心功能毫無(wú)關(guān)系。對(duì)于其他類(lèi)型的代碼,如安全性、異常處理和透明的持續(xù)性也是如此。這種散布在各處的無(wú)關(guān)的代碼被稱(chēng)為橫切(cross-cutting)代碼,在OOP設(shè)計(jì)中,它導(dǎo)致了大量代碼的重復(fù),而不利于各個(gè)模塊的重用。
而AOP技術(shù)則恰恰相反,它利用一種稱(chēng)為“橫切”的技術(shù),剖解開(kāi)封裝的對(duì)象內(nèi)部,并將那些影響了多個(gè)類(lèi)的公共行為封裝到一個(gè)可重用模塊,并將其名為“Aspect”,即方面。所謂“方面”,簡(jiǎn)單地說(shuō),就是將那些與業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性。AOP代表的是一個(gè)橫向的關(guān)系,如果說(shuō)“對(duì)象”是一個(gè)空心的圓柱體,其中封裝的是對(duì)象的屬性和行為;那么面向方面編程的方法,就仿佛一把利刃,將這些空心圓柱體剖開(kāi),以獲得其內(nèi)部的消息。而剖開(kāi)的切面,也就是所謂的“方面”了。然后它又以巧奪天功的妙手將這些剖開(kāi)的切面復(fù)原,不留痕跡。
使用“橫切”技術(shù),AOP把軟件系統(tǒng)分為兩個(gè)部分:核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)。業(yè)務(wù)處理的主要流程是核心關(guān)注點(diǎn),與之關(guān)系不大的部分是橫切關(guān)注點(diǎn)。橫切關(guān)注點(diǎn)的一個(gè)特點(diǎn)是,他們經(jīng)常發(fā)生在核心關(guān)注點(diǎn)的多處,而各處都基本相似。比如權(quán)限認(rèn)證、日志、事務(wù)處理。Aop 的作用在于分離系統(tǒng)中的各種關(guān)注點(diǎn),將核心關(guān)注點(diǎn)和橫切關(guān)注點(diǎn)分離開(kāi)來(lái)。正如Avanade公司的高級(jí)方案構(gòu)架師Adam Magee所說(shuō),AOP的核心思想就是“將應(yīng)用程序中的商業(yè)邏輯同對(duì)其提供支持的通用服務(wù)進(jìn)行分離。”
實(shí)現(xiàn)AOP的技術(shù),主要分為兩大類(lèi):一是采用動(dòng)態(tài)代理技術(shù),利用截取消息的方式,對(duì)該消息進(jìn)行裝飾,以取代原有對(duì)象行為的執(zhí)行;二是采用靜態(tài)織入的方式,引入特定的語(yǔ)法創(chuàng)建“方面”,從而使得編譯器可以在編譯期間織入有關(guān)“方面”的代碼。
AOP用來(lái)封裝橫切關(guān)注點(diǎn),具體可以在下面的場(chǎng)景中使用:
Authentication 權(quán)限
Caching 緩存
Context passing 內(nèi)容傳遞
Error handling 錯(cuò)誤處理
Lazy loading 懶加載
Debugging 調(diào)試
logging, tracing, profiling and monitoring 記錄跟蹤 優(yōu)化 校準(zhǔn)
Performance optimization 性能優(yōu)化
Persistence 持久化
Resource pooling 資源池
Synchronization 同步
Transactions 事務(wù)
下面這種類(lèi)圖列出了Spring中主要的AOP組件
可以通過(guò)配置文件或者編程的方式來(lái)使用Spring AOP。
配置可以通過(guò)xml文件來(lái)進(jìn)行,大概有四種方式:
1.配置ProxyFactoryBean,顯式地設(shè)置advisors, advice, target等
2.????????配置AutoProxyCreator,這種方式下,還是如以前一樣使用定義的bean,但是從容器中獲得的其實(shí)已經(jīng)是代理對(duì)象
3.????????通過(guò)<aop:config>來(lái)配置
4.????????通過(guò)<aop: aspectj-autoproxy>來(lái)配置,使用AspectJ的注解來(lái)標(biāo)識(shí)通知及切入點(diǎn)
也可以直接使用ProxyFactory來(lái)以編程的方式使用Spring AOP,通過(guò)ProxyFactory提供的方法可以設(shè)置target對(duì)象, advisor等相關(guān)配置,最終通過(guò) getProxy()方法來(lái)獲取代理對(duì)象
具體使用的示例可以google. 這里略去
Spring提供了兩種方式來(lái)生成代理對(duì)象: JDKProxy和Cglib,具體使用哪種方式生成由AopProxyFactory根據(jù)AdvisedSupport對(duì)象的配置來(lái)決定。默認(rèn)的策略是如果目標(biāo)類(lèi)是接口,則使用JDK動(dòng)態(tài)代理技術(shù),否則使用Cglib來(lái)生成代理。下面我們來(lái)研究一下Spring如何使用JDK來(lái)生成代理對(duì)象,具體的生成代碼放在JdkDynamicAopProxy這個(gè)類(lèi)中,直接上相關(guān)代碼:
[java]view plaincopy
/**
????*?<ol>
????*?<li>獲取代理類(lèi)要實(shí)現(xiàn)的接口,除了Advised對(duì)象中配置的,還會(huì)加上SpringProxy,?Advised(opaque=false)
????*?<li>檢查上面得到的接口中有沒(méi)有定義?equals或者h(yuǎn)ashcode的接口
????*?<li>調(diào)用Proxy.newProxyInstance創(chuàng)建代理對(duì)象
????*?</ol>
????*/??
public?Object?getProxy(ClassLoader?classLoader)?{??
if?(logger.isDebugEnabled())?{??
logger.debug("Creating?JDK?dynamic?proxy:?target?source?is?"?+this.advised.getTargetSource());??
???????}??
Class[]?proxiedInterfaces?=AopProxyUtils.completeProxiedInterfaces(this.advised);??
???????findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);??
return?Proxy.newProxyInstance(classLoader,?proxiedInterfaces,?this);??
}??
那這個(gè)其實(shí)很明了,注釋上我也已經(jīng)寫(xiě)清楚了,不再贅述。
下面的問(wèn)題是,代理對(duì)象生成了,那切面是如何織入的?
我們知道InvocationHandler是JDK動(dòng)態(tài)代理的核心,生成的代理對(duì)象的方法調(diào)用都會(huì)委托到InvocationHandler.invoke()方法。而通過(guò)JdkDynamicAopProxy的簽名我們可以看到這個(gè)類(lèi)其實(shí)也實(shí)現(xiàn)了InvocationHandler,下面我們就通過(guò)分析這個(gè)類(lèi)中實(shí)現(xiàn)的invoke()方法來(lái)具體看下Spring AOP是如何織入切面的。
[java]view plaincopy
publicObject?invoke(Object?proxy,?Method?method,?Object[]?args)?throwsThrowable?{??
MethodInvocation?invocation?=null;??
Object?oldProxy?=null;??
boolean?setProxyContext?=?false;??
TargetSource?targetSource?=this.advised.targetSource;??
Class?targetClass?=null;??
Object?target?=null;??
try?{??
//eqauls()方法,具目標(biāo)對(duì)象未實(shí)現(xiàn)此方法??
if?(!this.equalsDefined?&&?AopUtils.isEqualsMethod(method)){??
return?(equals(args[0])??Boolean.TRUE?:?Boolean.FALSE);??
???????????}??
//hashCode()方法,具目標(biāo)對(duì)象未實(shí)現(xiàn)此方法??
if?(!this.hashCodeDefined?&&?AopUtils.isHashCodeMethod(method)){??
return?newInteger(hashCode());??
???????????}??
//Advised接口或者其父接口中定義的方法,直接反射調(diào)用,不應(yīng)用通知??
if?(!this.advised.opaque?&&method.getDeclaringClass().isInterface()??
&&method.getDeclaringClass().isAssignableFrom(Advised.class))?{??
//?Service?invocations?onProxyConfig?with?the?proxy?config...??
return?AopUtils.invokeJoinpointUsingReflection(this.advised,method,?args);??
???????????}??
Object?retVal?=null;??
if?(this.advised.exposeProxy)?{??
//?Make?invocation?available?ifnecessary.??
????????????????oldProxy?=?AopContext.setCurrentProxy(proxy);??
setProxyContext?=true;??
???????????}??
//獲得目標(biāo)對(duì)象的類(lèi)??
???????????target?=?targetSource.getTarget();??
if?(target?!=?null)?{??
????????????????targetClass?=?target.getClass();??
???????????}??
//獲取可以應(yīng)用到此方法上的Interceptor列表??
List?chain?=this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);??
//如果沒(méi)有可以應(yīng)用到此方法的通知(Interceptor),此直接反射調(diào)用?method.invoke(target,?args)??
if?(chain.isEmpty())?{??
????????????????retVal?=?AopUtils.invokeJoinpointUsingReflection(target,method,?args);??
}else?{??
//創(chuàng)建MethodInvocation??
????????????????invocation?=?newReflectiveMethodInvocation(proxy,?target,?method,?args,?targetClass,?chain);??
????????????????retVal?=?invocation.proceed();??
???????????}??
//?Massage?return?value?if?necessary.??
if?(retVal?!=?null?&&?retVal?==?target?&&method.getReturnType().isInstance(proxy)??
&&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass()))?{??
//?Special?case:?it?returned"this"?and?the?return?type?of?the?method??
//?is?type-compatible.?Notethat?we?can't?help?if?the?target?sets??
//?a?reference?to?itself?inanother?returned?object.??
????????????????retVal?=?proxy;??
???????????}??
return?retVal;??
}finally?{??
if?(target?!=?null?&&?!targetSource.isStatic())?{??
//?Must?have?come?fromTargetSource.??
???????????????targetSource.releaseTarget(target);??
???????????}??
if?(setProxyContext)?{??
//?Restore?old?proxy.??
????????????????AopContext.setCurrentProxy(oldProxy);??
???????????}??
???????}??
????}??
主流程可以簡(jiǎn)述為:獲取可以應(yīng)用到此方法上的通知鏈(Interceptor Chain),如果有,則應(yīng)用通知,并執(zhí)行joinpoint; 如果沒(méi)有,則直接反射執(zhí)行joinpoint。而這里的關(guān)鍵是通知鏈?zhǔn)侨绾潍@取的以及它又是如何執(zhí)行的,下面逐一分析下。
首先,從上面的代碼可以看到,通知鏈?zhǔn)峭ㄟ^(guò)Advised.getInterceptorsAndDynamicInterceptionAdvice()這個(gè)方法來(lái)獲取的,我們來(lái)看下這個(gè)方法的實(shí)現(xiàn):
[java]view plaincopy
public?List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method?method,?Class?targetClass)?{??
MethodCacheKeycacheKey?=new?MethodCacheKey(method);??
Listcached?=this.methodCache.get(cacheKey);??
if(cached?==?null)?{??
cached=this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(??
this,method,?targetClass);??
this.methodCache.put(cacheKey,cached);??
???????????????????}??
???????????????????returncached;??
?????????}??
可以看到實(shí)際的獲取工作其實(shí)是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()這個(gè)方法來(lái)完成的,獲取到的結(jié)果會(huì)被緩存。
下面來(lái)分析下這個(gè)方法的實(shí)現(xiàn):
[java]view plaincopy
/**
????*?從提供的配置實(shí)例config中獲取advisor列表,遍歷處理這些advisor.如果是IntroductionAdvisor,
????*?則判斷此Advisor能否應(yīng)用到目標(biāo)類(lèi)targetClass上.如果是PointcutAdvisor,則判斷
????*?此Advisor能否應(yīng)用到目標(biāo)方法method上.將滿(mǎn)足條件的Advisor通過(guò)AdvisorAdaptor轉(zhuǎn)化成Interceptor列表返回.
????*/??
????publicList?getInterceptorsAndDynamicInterceptionAdvice(Advised?config,?Methodmethod,?Class?targetClass)?{??
//?This?is?somewhat?tricky...?we?have?to?process?introductions?first,??
//?but?we?need?to?preserve?order?in?the?ultimate?list.??
List?interceptorList?=new?ArrayList(config.getAdvisors().length);??
//查看是否包含IntroductionAdvisor??
boolean?hasIntroductions?=?hasMatchingIntroductions(config,targetClass);??
//這里實(shí)際上注冊(cè)一系列AdvisorAdapter,用于將Advisor轉(zhuǎn)化成MethodInterceptor??
???????AdvisorAdapterRegistry?registry?=?GlobalAdvisorAdapterRegistry.getInstance();??
???????Advisor[]?advisors?=?config.getAdvisors();??
for?(int?i?=?0;?i?<advisors.length;?i++)?{??
???????????Advisor?advisor?=?advisors[i];??
if?(advisor?instanceof?PointcutAdvisor)?{??
//?Add?it?conditionally.??
????????????????PointcutAdvisor?pointcutAdvisor=?(PointcutAdvisor)?advisor;??
if(config.isPreFiltered()?||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass))?{??
//TODO:?這個(gè)地方這兩個(gè)方法的位置可以互換下??
//將Advisor轉(zhuǎn)化成Interceptor??
????????????????????MethodInterceptor[]interceptors?=?registry.getInterceptors(advisor);??
//檢查當(dāng)前advisor的pointcut是否可以匹配當(dāng)前方法??
????????????????????MethodMatcher?mm?=pointcutAdvisor.getPointcut().getMethodMatcher();??
if?(MethodMatchers.matches(mm,method,?targetClass,?hasIntroductions))?{??
if(mm.isRuntime())?{??
//?Creating?a?newobject?instance?in?the?getInterceptors()?method??
//?isn't?a?problemas?we?normally?cache?created?chains.??
for?(intj?=?0;?j?<?interceptors.length;?j++)?{??
interceptorList.add(new?InterceptorAndDynamicMethodMatcher(interceptors[j],mm));??
????????????????????????????}??
}else?{??
????????????????????????????interceptorList.addAll(Arrays.asList(interceptors));??
????????????????????????}??
????????????????????}??
????????????????}??
}else?if?(advisor?instanceof?IntroductionAdvisor){??
????????????????IntroductionAdvisor?ia?=(IntroductionAdvisor)?advisor;??
if(config.isPreFiltered()?||?ia.getClassFilter().matches(targetClass))?{??
????????????????????Interceptor[]?interceptors=?registry.getInterceptors(advisor);??
????????????????????interceptorList.addAll(Arrays.asList(interceptors));??
????????????????}??
}else?{??
????????????????Interceptor[]?interceptors?=registry.getInterceptors(advisor);??
????????????????interceptorList.addAll(Arrays.asList(interceptors));??
???????????}??
???????}??
return?interceptorList;??
}??
這個(gè)方法執(zhí)行完成后,Advised中配置能夠應(yīng)用到連接點(diǎn)或者目標(biāo)類(lèi)的Advisor全部被轉(zhuǎn)化成了MethodInterceptor.
接下來(lái)我們?cè)倏聪碌玫降臄r截器鏈?zhǔn)窃趺雌鹱饔玫摹?/p>
[java]view plaincopy
if?(chain.isEmpty())?{??
????????????????retVal?=?AopUtils.invokeJoinpointUsingReflection(target,method,?args);??
}else?{??
//創(chuàng)建MethodInvocation??
????????????????invocation?=?newReflectiveMethodInvocation(proxy,?target,?method,?args,?targetClass,?chain);??
????????????????retVal?=?invocation.proceed();??
????????????}??
???????? 從這段代碼可以看出,如果得到的攔截器鏈為空,則直接反射調(diào)用目標(biāo)方法,否則創(chuàng)建MethodInvocation,調(diào)用其proceed方法,觸發(fā)攔截器鏈的執(zhí)行,來(lái)看下具體代碼
[java]view plaincopy
public?Object?proceed()?throws?Throwable?{??
//??We?start?with?an?index?of?-1and?increment?early.??
if?(this.currentInterceptorIndex?==?this.interceptorsAndDynamicMethodMatchers.size()-?1)?{??
//如果Interceptor執(zhí)行完了,則執(zhí)行joinPoint??
return?invokeJoinpoint();??
???????}??
???????Object?interceptorOrInterceptionAdvice?=??
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);??
//如果要?jiǎng)討B(tài)匹配joinPoint??
if?(interceptorOrInterceptionAdvice?instanceof?InterceptorAndDynamicMethodMatcher){??
//?Evaluate?dynamic?method?matcher?here:?static?part?will?already?have??
//?been?evaluated?and?found?to?match.??
???????????InterceptorAndDynamicMethodMatcher?dm?=??
????????????????(InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;??
//動(dòng)態(tài)匹配:運(yùn)行時(shí)參數(shù)是否滿(mǎn)足匹配條件??
if?(dm.methodMatcher.matches(this.method,?this.targetClass,this.arguments))?{??
//執(zhí)行當(dāng)前Intercetpor??
returndm.interceptor.invoke(this);??
???????????}??
else?{??
//動(dòng)態(tài)匹配失敗時(shí),略過(guò)當(dāng)前Intercetpor,調(diào)用下一個(gè)Interceptor??
return?proceed();??
???????????}??
???????}??
else?{??
//?It's?an?interceptor,?so?we?just?invoke?it:?The?pointcutwill?have??
//?been?evaluated?statically?before?this?object?was?constructed.??
//執(zhí)行當(dāng)前Intercetpor??
return?((MethodInterceptor)?interceptorOrInterceptionAdvice).invoke(this);??
???????}??
好處:
解耦:實(shí)現(xiàn)低耦合高內(nèi)聚;
實(shí)現(xiàn)代碼復(fù)用,提高使用效率。