Spring AOP是怎么回事?
哈嘍,大家好,我是了不起。
前面我們看過javaassit是如何破解java應(yīng)用,核心都是AOP相關(guān)的知識(shí),今天我們看下Spring AOP是怎么回事!
Spring-AOP
AOP面向切面編程,通過預(yù)編譯方式和運(yùn)行期間動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP是OOP的延續(xù),從另一視角擴(kuò)展了對面向?qū)ο缶幊痰男问?。利用AOP可以對業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開發(fā)的效率。
Spring AOP與IOC作為整個(gè)Spring框架最為核心的兩個(gè)部分,其意義不言而喻。在Spring中,我們都是面向Bean的裝配與管理,一個(gè)Bean是一個(gè)對象,也可以是一個(gè)代理。
概念
Aspect(切面):Aspect聲明類似于Java中的類聲明,在Aspect中會(huì)包含著一些Pointcut以及相應(yīng)的 Advice。
JointPoint(連接點(diǎn)):表示在程序中明確定義的點(diǎn),典型的包括方法調(diào)用,對類成員的訪問以及異常處理程序塊的執(zhí)行等等,它自身還可以嵌套其它joint point。
Pointcut(切點(diǎn)):按規(guī)則匹配的JointPoint,這些JointPoint或是通過邏輯關(guān)系組合起來,或是通過通配、正則表達(dá)式等方式集中起來,它定義了相應(yīng)的Advice執(zhí)行的具體地方。
Advice(通知):Advice定義了在Pointcut里面定義的程序點(diǎn)具體要做的操作,它通過before、after和around來區(qū)別是在每個(gè)JointPoint之前、之后還是代替執(zhí)行的代碼。
Target(目標(biāo)對象):將Advice織入到目標(biāo)對象.。
Weaving(織入):將Aspect和其他對象連接起來, 并創(chuàng)建增強(qiáng)(代理)對象的過程
下面從幾個(gè)方面了解Spring中如何使用AOP,你會(huì)發(fā)現(xiàn),所有的配置都是圍繞著這些概念。Spring提供了AOP代理的上下文環(huán)境,而對目標(biāo)對象的加強(qiáng)(Aspect、Advisor)都是由開發(fā)者自己完成。
Spring-aop.xml
- 基于aspect配置AOP,一個(gè)Aspect包含Pointcut與Advice:
- 基于advice配置AOP,這種方式更方便Advice的復(fù)用
@Aspect注解
基于注解@Aspect定義切面來增強(qiáng)目標(biāo)對象,與上面的XML第一種配置對應(yīng),記得使用@EnableAspectJAutoProxy注解開啟
AOP代理執(zhí)行順序
spring 5.2.7之后的執(zhí)行順序:
Pointcut表達(dá)式(AspectJ)
execution:用于匹配方法執(zhí)行的連接點(diǎn)
within:用于匹配指定類型內(nèi)的方法執(zhí)行
this:用于匹配當(dāng)前AOP代理對象類型的執(zhí)行方法;注意是AOP代理對象的類型匹配,這樣就可能包括引入接口也類型匹配
target:用于匹配當(dāng)前目標(biāo)對象類型的執(zhí)行方法;注意是目標(biāo)對象的類型匹配,這樣就不包括引入接口也類型匹配
args:用于匹配當(dāng)前執(zhí)行的方法傳入的參數(shù)為指定類型的執(zhí)行方法
@within:用于匹配所以持有指定注解類型內(nèi)的方法
@target:用于匹配當(dāng)前目標(biāo)對象類型的執(zhí)行方法,其中目標(biāo)對象持有指定的注解
@args:用于匹配當(dāng)前執(zhí)行的方法傳入的參數(shù)持有指定注解的執(zhí)行
@annotation:用于匹配當(dāng)前執(zhí)行方法持有指定注解的方法
bean:Spring AOP擴(kuò)展的,AspectJ沒有對于指示符,用于匹配特定名稱的Bean對象的執(zhí)行方法
基于@Configuration
通過ProxyFactoryBean我們可以生成基于目標(biāo)對象的代理。通過下面幾行代碼,加上自定義的切面實(shí)現(xiàn)(MethodInterceptor、Advice等接口的實(shí)現(xiàn)),就可以實(shí)現(xiàn)上面的before、after、around等通知切面。
實(shí)現(xiàn)原理
這里說到的是上面第二種通過@Aspect的形式實(shí)現(xiàn)AOP功能,這種模式更適合業(yè)務(wù)模塊中對特定模塊內(nèi)的方法進(jìn)行業(yè)務(wù)代理。我們需要依賴注解EnableAspectJAutoProxy, 下面來看看具體如何實(shí)現(xiàn)?
- 在EnableAspectJAutoProxy中可以看到@Import(AspectJAutoProxyRegistrar.class),如果你之前看過之前的spring boot start你就知道,這里是基于ImportBeanDefinitionRegistrar在Spring容器中注冊BeanDefinition。
- 可以看到AspectJAutoProxyRegistrar#registerBeanDefinitions第一行,AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry), 主要是注冊了BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)。
- 我們知道在Spring Bean生命中期中,Bean執(zhí)行初始化前后會(huì)執(zhí)行容器中的nPostProcessor,Spring AOP即通過AnnotationAwareAspectJAutoProxyCreator這個(gè)BeanPostProcessor完成Bean的代理工作。
- 其父類AbstractAutoProxyCreator中,postProcessAfterInitialization -> wrapIfNecessary -> createProxy -> ProxyFactory -> getProxy 到這里,Bean的代理對象也就生成了, 當(dāng)然省略了各種判斷以及加工過程。
代理方式
- Cglib和JDK ProxySpring AOP主要是通過這兩個(gè)代理框架來實(shí)現(xiàn)代理的。一般情況下,基于接口代理時(shí)使用JDK動(dòng)態(tài)代理,否則使用Cglib.
Java動(dòng)態(tài)代理只能夠?qū)涌谶M(jìn)行代理,不能對普通的類進(jìn)行代理(因?yàn)樗猩傻拇眍惖母割悶镻roxy,Java類繼承機(jī)制不允許多重繼承)。而CGLIB能夠代理普通類
Java動(dòng)態(tài)代理使用Java原生的反射API進(jìn)行操作,在生成類上比較高效;CGLIB使用ASM框架直接對字節(jié)碼進(jìn)行操作,在類的執(zhí)行過程中比較高效
- AspectJAspectJ是一個(gè)功能強(qiáng)大的面相切面編程框架,是對Java面向?qū)ο蟮臄U(kuò)展,支持編譯時(shí)、編譯后、加載時(shí)為目標(biāo)對象(不僅僅是類方法)織入代理。在Spring AOP中引入了aspectjweaver.jar,僅僅使用了其注解。
下面是網(wǎng)上Spring AOP與AspectJ的比對
Spring AOP | AspectJ |
用純Java實(shí)現(xiàn) | 使用Java編程語言的擴(kuò)展實(shí)現(xiàn) |
無需單獨(dú)的編譯過程 | 除非設(shè)置了LTW,否則需要AspectJ編譯器(ajc) |
僅需運(yùn)行時(shí)編織 | 運(yùn)行時(shí)編織不可用。支持編譯時(shí),后編譯和加載時(shí)編織 |
不足–僅支持方法級編織 | 更強(qiáng)大–可以編織字段,方法,構(gòu)造函數(shù),靜態(tài)初始值設(shè)定項(xiàng),最終類/方法等 |
只能在Spring容器管理的bean上實(shí)現(xiàn) | 可以在所有領(lǐng)域?qū)ο笊蠈?shí)施 |
僅支持方法執(zhí)行切入點(diǎn) | 支持所有切入點(diǎn) |
代理是針對目標(biāo)對象創(chuàng)建的,并且方面已應(yīng)用于這些代理 | 在應(yīng)用程序執(zhí)行之前(運(yùn)行時(shí)之前)將方面直接編織到代碼中 |
比AspectJ慢得多 | 更好的性能 |
易于學(xué)習(xí)和應(yīng)用 | 比Spring AOP復(fù)雜得多 |
結(jié)束語
Spring AOP是整個(gè)Spring框架的核心,里面涉及到的內(nèi)容不是很多,但是都比較有深度,在spring諸多模塊中,比如事務(wù)、緩存、鑒權(quán)等等方面都有使用到。由于springboot的出現(xiàn),xml的配置形式使用 得比較少了,但是這種配置的形式更直白地體現(xiàn)了AOP需要的配置以及各個(gè)組件的依賴關(guān)系。而基于@Aspect的形式代理業(yè)務(wù)模塊中的方法更簡單直觀,而基于ProxyFactoryBean、ProxyFactory的方式,在編寫類似 cache的模塊會(huì)更加的靈活。