梳理一下我理解的aop
在看了很多網(wǎng)上的資料和記錄之后,我大概捋了下SpringAOP的各種階段:
?
先記錄一下AOP的幾個(gè)概念:
AOP:
首先,AOP是Aspect-Originted Programming,即面向切面編程。我覺(jué)得可以這樣地簡(jiǎn)單理解,aop就是為了你更清楚的邏輯,讓你的業(yè)務(wù)邏輯代碼更清晰,不用去想其他事,像日志啊,權(quán)限啊這些和業(yè)務(wù)邏輯無(wú)關(guān)的東西。那要怎么用這些又很重要的功能呢?我們不用改變?cè)瓉?lái)的代碼,只要在另一個(gè)個(gè)地方,把這些要附加的功能打包好,然后運(yùn)行的時(shí)候切進(jìn)你要用這些功能的地方,aop其實(shí)大概就是這樣。好的那就來(lái)看看下面的幾個(gè)名詞:
advice:
翻譯成增強(qiáng)、通知,其實(shí)就是你要做的東西,你要切到你的代碼中的功能。aop框架會(huì)把a(bǔ)dvice模擬成攔截器interceptor。
Join Point:
連接點(diǎn),就是你可以加入功能,加入advice的地方。Spring只支持在方法或者是拋出異常的地方建立使用通知,也就是說(shuō)這些地方都可以是joinpoint,像其他aop像aspectJ還可以讓你在構(gòu)造器或?qū)傩宰⑷霑r(shí)加入advice。
Pointcut:
剛剛JoinPoint我們說(shuō)了哪些地方可以用advice,但不是所有地方都要用advice啊,所以就有了這個(gè)pointcut,就是加上條件的joincut。在寫(xiě)代碼的時(shí)候,會(huì)用類似正則表達(dá)式的方式去選擇那些joinpoint來(lái)作為pointcut。
Aspect:
切面,這個(gè)可是個(gè)很重要的概念哦。其實(shí)就是advice+pointcut。Spring AOP就是負(fù)責(zé)實(shí)施切面的框架, 它將切面所定義的橫切邏輯織入到切面所指定的連接點(diǎn)中.?
AOP的工作重心在于如何將增強(qiáng)織入目標(biāo)對(duì)象的連接點(diǎn)上, 這里包含兩個(gè)工作:
Introduction:
翻譯成引入,就是引入允許我們向現(xiàn)有的類添加新方法或?qū)傩?#xff0c;從而無(wú)需修改這些現(xiàn)有類的情況下,讓他們具有新的行為和狀態(tài)。Spring AOP 允許我們?yōu)?目標(biāo)對(duì)象 引入新的接口(和對(duì)應(yīng)的實(shí)現(xiàn)). 例如我們可以使用 introduction 來(lái)為一個(gè) bean 實(shí)現(xiàn) IsModified 接口, 并以此來(lái)簡(jiǎn)化 caching 的實(shí)現(xiàn).
Target:
目標(biāo),要加入新功能,新advice的目標(biāo)類。就是不知道什么情況然后等等要被我們切入新功能的那個(gè)類。
AOP proxy:
一個(gè)類被 AOP 織入 advice, 就會(huì)產(chǎn)生一個(gè)結(jié)果類, 它是融合了原類和增強(qiáng)邏輯的代理類.?在 Spring AOP 中, 一個(gè) AOP 代理是一個(gè) JDK 動(dòng)態(tài)代理對(duì)象或 CGLIB 代理對(duì)象.? ?可以說(shuō),spring aop就是用動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)的,我理解的,就是我們要在一個(gè)target類中加入新功能嘛,就通過(guò)一個(gè)代理類來(lái)實(shí)現(xiàn),代理類是原來(lái)目標(biāo)類+advice的結(jié)合,可以想象成變強(qiáng)了的target類的替身。那么要怎樣才能成為這個(gè)替身呢?要么就實(shí)現(xiàn)一樣的接口(jdk動(dòng)態(tài)代理的原理),要么就繼承target類稱為它的子類(CGLIB的原理)。
Weaving:
織入,將aspect和其他對(duì)象連接起來(lái),并創(chuàng)建adviced object的過(guò)程。(可以把weaving理解成動(dòng)詞,introduction理解成名詞)。根據(jù)不同的技術(shù),weaving的方式有三種:編譯器織入,要求有特制的編譯器;類裝載期織入,這需要特殊的類裝載期器;動(dòng)態(tài)代理織入,在運(yùn)行期間為target添加advice的方式。
?
一、先是非常原始的advice實(shí)現(xiàn)
這里我們寫(xiě)一個(gè)非常原始的,連spring bean都不用的例子來(lái)幫助理解aop。寫(xiě)的是advice增強(qiáng),用的是jdk代理的方式,也就是接口代理。但要先引入Spring aop的相關(guān)jar包
思路就是,要加強(qiáng)的類實(shí)現(xiàn)一個(gè)接口,然后advice作為加強(qiáng)類,要實(shí)現(xiàn)SpringAOP提供的Advice相關(guān)接口。然后通過(guò)ProxyFactory來(lái)拿代理類,往代理類中addAdvice來(lái)達(dá)到加強(qiáng)的效果。
?
這里我們有個(gè)Greeting接口。
package com.stuPayment.aopTest;public interface Greeting {public void sayHello(String name);public int saySth(); }然后它的實(shí)現(xiàn):
package com.stuPayment.aopTest;public class GreetingImpl implements Greeting {@Overridepublic void sayHello(String name) {// TODO Auto-generated method stubSystem.out.println("hello, " + name);}@Overridepublic int saySth() {// TODO Auto-generated method stubSystem.out.println("this is say sth");return 213;}public int sayMorning(String name) {System.out.println("good morning, " + name);return 123;}public void sayAfternoon(String name) {System.out.println("good afternoon, " + name);} }然后是我們的advice增強(qiáng)類,要這里我們用的是MethodBeforeAdvice接口和AfterReturningMethod,表示著advice是加在方法調(diào)用前還是方法返回之后。
package com.stuPayment.aopTest.advice;import java.lang.reflect.Method;import org.springframework.aop.AfterReturningAdvice; import org.springframework.aop.MethodBeforeAdvice;public class GreetingBeforeAndAfterAdvice implements MethodBeforeAdvice, AfterReturningAdvice{@Overridepublic void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {// TODO Auto-generated method stubSystem.out.println("this is after");}@Overridepublic void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {// TODO Auto-generated method stubSystem.out.println("this is before");}}然后測(cè)試類:
package com.stuPayment.aopTest;import org.junit.Test; import org.springframework.aop.framework.ProxyFactory;import com.stuPayment.aopTest.advice.GreetingAroundAdvice; import com.stuPayment.aopTest.advice.GreetingBeforeAndAfterAdvice;public class Test1 {@Testpublic void demo1() {ProxyFactory proxyFactory = new ProxyFactory();//創(chuàng)建代理工廠proxyFactory.setTarget(new GreetingImpl());//注入目標(biāo)類對(duì)象proxyFactory.addAdvice(new GreetingBeforeAndAfterAdvice());//添加前置加強(qiáng)和后置加強(qiáng)//proxyFactory.addAdvice(new GreetingAroundAdvice());//添加前置加強(qiáng)和后置加強(qiáng)Greeting greeting = (Greeting)proxyFactory.getProxy();//從代理工廠中獲取代理greeting.sayHello("Ben");//調(diào)用代理的方法 greeting.saySth();} }?
結(jié)果:
所以大概思路就是,編寫(xiě)一個(gè)增強(qiáng)類去實(shí)現(xiàn)Spring aop提供的幾個(gè)advice的接口(其實(shí)這些接口就是決定在接入點(diǎn)的哪個(gè)位置加入新advice),然用aop給的ProxyFactory,先設(shè)定一個(gè)target,然后addAdvice來(lái)加入你要切入的功能,然后你就可以通過(guò)這個(gè)代理工廠來(lái)獲得一個(gè)代理類(對(duì)應(yīng)接口的),一個(gè)加強(qiáng)后的adviced object,調(diào)用它里面方法就會(huì)看到advice的效果。
?
然后SpringAop給了好幾種Advice的增強(qiáng)接口:
?
?
?
?
二、一般,到了后面,我們會(huì)把這個(gè)Proxy的配置加入Spring的配置文件中
這個(gè)greetingAroundAdvice就像我們剛剛那個(gè)GreetingBeforeAndAfterAdvice一樣是個(gè)實(shí)現(xiàn)了一個(gè)advice接口的增強(qiáng)類。
?
?
這里再介紹一下一個(gè)Introduction Advice,來(lái)看看jdk動(dòng)態(tài)代理和CGLIB類代理的區(qū)別。
上面說(shuō)到,這個(gè)引介增強(qiáng)Introduction Advice是一種特殊的增強(qiáng),之前的連接點(diǎn)都是方法級(jí)別的,而這個(gè)是類級(jí)別的,也就是對(duì)類的加強(qiáng)。
?
引入增強(qiáng)Introduction Advice的概念:一個(gè)Java類,沒(méi)有實(shí)現(xiàn)A接口,在不修改Java類的情況下,使其具備A接口的功能。
先定義一個(gè)新接口Love:
?
然后定義授權(quán)引入增強(qiáng)類:
這個(gè)DelegatingIntroductionInterceptor就是一個(gè)引入的advice類,繼承它就有這種advice的能力。
?
然后是Proxy的配置:
?
?proxyTargetClass屬性表示是否代理目標(biāo)類,默認(rèn)是false,也就是代理接口,上面一個(gè)例子的配置就是沒(méi)有這一項(xiàng)屬性所以用JDK動(dòng)態(tài)代理,現(xiàn)在是true即使用CGLib動(dòng)態(tài)代理。
?
然后看測(cè)試方法里面:
首先看到,從代理里面拿這個(gè)GreetingImpl的時(shí)候,不是像以前一樣:Greeting greeting = (Greeting).......
而是直接用實(shí)現(xiàn)類GreetingImpl來(lái)拿。因?yàn)楝F(xiàn)在是代理目標(biāo)類而不是接口類。
然后是這里的Love love = (Love)greetingImpl是將目標(biāo)類強(qiáng)制向上轉(zhuǎn)換成了Love接口,但注意,我們并沒(méi)有把這個(gè)greetingImpl實(shí)現(xiàn)這個(gè)Love接口哦,這就是引用增強(qiáng)(DelegatingIntroductionInterceptor)的特性——“接口動(dòng)態(tài)實(shí)現(xiàn)的”功能。所以display()方法可以由GreetingImpl的對(duì)象來(lái)調(diào)用,只需要強(qiáng)行轉(zhuǎn)換接口就行了。
?
?
?
三、好了,在advice的層次下的編程之后,就到了后面aspect切面的編程了:
所謂切面,其實(shí)就是advice+pointcut,我們?cè)谇忻嬷幸话阋龅木褪嵌x要加的功能還有正則表達(dá)式確定要攔截的方法。
這里開(kāi)始內(nèi)容就很多了,也有點(diǎn)亂,這里稍微捋一下,遲點(diǎn)會(huì)通過(guò)閱讀相關(guān)的書(shū)籍來(lái)理清楚關(guān)系。
?
切面編程一開(kāi)始,我們可以通過(guò)springAOP提供的切面類RegexpMethodPointcutAdvisor來(lái)配置切面。一樣還是剛剛的實(shí)現(xiàn)了Greeting的GreetingImpl作為target,配置文件如下:
這上面的proxy中的配置中的InterceptorNames,不再是之前的advice加強(qiáng)了,而是一個(gè)定義好的切面。我們看這個(gè)greetingAdvisor的bean的配置,可以看到一個(gè)屬性是advice,而另一個(gè)是pattern,其實(shí)就相當(dāng)于要加強(qiáng)的功能和pointcut。
?
?
再往后發(fā)展,proxy也不用怎么寫(xiě)配置了,有了個(gè)叫aop自動(dòng)代理的東西,大概就是spring框架自動(dòng)生成代理:
配置文件:(屬性optimize為true,表示,如果target有接口,就用jdk動(dòng)態(tài)代理,若谷target沒(méi)有接口,就用CGLib動(dòng)態(tài)代理)
還有測(cè)試代碼:
此時(shí)因?yàn)槭亲詣?dòng)代理,getBean()的值不再是原來(lái)的代理id(greetingProxy),而是目標(biāo)類GreetingImpl的bean的id,這同樣也是個(gè)代理類,只是自動(dòng)代理,隱藏了代理的工作和代碼。
?
?
再往后,就到了AspectJ風(fēng)格的切面編程,用注解就可以完成切面的編程,大大節(jié)省了配置的時(shí)間。
?
?
先是利用<aop:config>元素聲明切面的方法,配置文件類似于下圖的xml:
?
?也可以直接使用@Aspect注解,只需要配置文件中簡(jiǎn)單得配置一下自動(dòng)代理:
<context:component-scan base-package="demo.spring"/> <!-- 用@Component自動(dòng)發(fā)布bean,需要配置這個(gè)元素。 --><aop:aspectj-autoproxy /> <!-- 使用@AspectJ及其它AOP注解需要配置,否則無(wú)法使用注解;@AspectJ注解,將@Component自動(dòng)發(fā)布出來(lái)的"interceptor" bean轉(zhuǎn)換為一個(gè)aspectj切面,而@Pointcut、@Before、@After、@Around等注解,功能與在xml文件中配置是一樣的;@Pointcut注解下面的方法內(nèi)容無(wú)意義,只是要求一個(gè)相應(yīng)方法提供注解依附。 -->注解只能在使用能獲得源碼的場(chǎng)景,如果不能獲取源碼,則只能通過(guò)xml配置的形式,將指定的對(duì)象配置成攔截器,對(duì)指定的目標(biāo)進(jìn)行攔截;因此,通過(guò)xml文件配置,而不是注解,是更加通用的方式。
?
?
?
然后再下一篇博客,會(huì)記錄如何在Springboot用AspectJ風(fēng)格的注解來(lái)用SpringAOP實(shí)現(xiàn)一個(gè)日志記錄的功能。
?
?
參考過(guò)的博客:
https://blog.csdn.net/h525483481/article/details/79625718
https://blog.csdn.net/icarus_wang/article/details/51737474 講增強(qiáng)類型的
https://www.cnblogs.com/jacksonshi/p/5863313.html
?
轉(zhuǎn)載于:https://www.cnblogs.com/wangshen31/p/9375231.html
總結(jié)
以上是生活随笔為你收集整理的梳理一下我理解的aop的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 开发心得
- 下一篇: HDU2050 折线分割平面