什么是AOP
AOP(Aspect-OrientedProgramming,面向方面編程),可以說(shuō)是OOP(Object-Oriented Programing,面向?qū)ο缶幊蹋┑难a(bǔ)充和完善。OOP允許你定義從上到下的關(guān)系,但并不適合定義從左到右的關(guān)系。例如日志功能。日志代碼往往水平地散布在所有對(duì)象層次中,而與它所散布到的對(duì)象的核心功能毫無(wú)關(guān)系。這種散布在各處的無(wú)關(guān)的代碼被稱為橫切(cross-cutting)代碼,在OOP設(shè)計(jì)中,它導(dǎo)致了大量代碼的重復(fù),而不利于各個(gè)模塊的重用。
而AOP技術(shù)則恰恰相反,它利用一種稱為“橫切”的技術(shù),剖解開(kāi)封裝的對(duì)象內(nèi)部,并將那些影響了多個(gè)類的公共行為封裝到一個(gè)可重用模塊,并將其名為“Aspect”,即方面。所謂“方面”,簡(jiǎn)單地說(shuō),就是將那些與業(yè)務(wù)無(wú)關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責(zé)任封裝起來(lái),便于減少系統(tǒng)的重復(fù)代碼,降低模塊間的耦合度,并有利于未來(lái)的可操作性和可維護(hù)性。
aop使用場(chǎng)景
aop框架種類
- AspectJ
- JBoss AOP
- Spring AOP
使用aop可以做的事情有很多。
- 性能監(jiān)控,在方法調(diào)用前后記錄調(diào)用時(shí)間,方法執(zhí)行太長(zhǎng)或超時(shí)報(bào)警。
- 緩存代理,緩存某方法的返回值,下次執(zhí)行該方法時(shí),直接從緩存里獲取。
- 軟件破解,使用AOP修改軟件的驗(yàn)證類的判斷邏輯。
- 記錄日志,在方法執(zhí)行前后記錄系統(tǒng)日志。
- 工作流系統(tǒng),工作流系統(tǒng)需要將業(yè)務(wù)代碼和流程引擎代碼混合在一起執(zhí)行,那么我們可以使用AOP將其分離,并動(dòng)態(tài)掛接業(yè)務(wù)。
- 權(quán)限驗(yàn)證,方法執(zhí)行前驗(yàn)證是否有權(quán)限執(zhí)行當(dāng)前方法,沒(méi)有則拋出沒(méi)有權(quán)限執(zhí)行異常,由業(yè)務(wù)代碼捕捉。
觀察一下傳統(tǒng)編碼方式與使用aop的區(qū)別
核心概念
描述AOP常用的一些術(shù)語(yǔ)有通知(Adivce)、切點(diǎn)(Pointcut)、連接點(diǎn)(Join point)、切面(Aspect)、引入(Introduction)、織入(Weaving)、通知(Advice)等。
簡(jiǎn)單例子
相比xml配置,基于注解的方式更加簡(jiǎn)潔方便。
@Aspect
public class TransactionDemo {
@Pointcut(value="execution(* com.yangxin.core.service.*.*.*(..))")
public void point(){
}
@Before(value="point()")
public void before(){
System.out.println("transaction begin");
}
@AfterReturning(value = "point()")
public void after(){
System.out.println("transaction commit");
}
@Around("point()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("transaction begin");
joinPoint.proceed();
System.out.println("transaction commit");
}
}
在applicationContext.xml中配置。
<aop:aspectj-autoproxy />
<bean id = "transactionDemo" class = "com.yangxin.core.transaction.TransactionDemo" />
spring aop原理
通過(guò)前面介紹可以知道:AOP 代理其實(shí)是由 AOP 框架動(dòng)態(tài)生成的一個(gè)對(duì)象,該對(duì)象可作為目標(biāo)對(duì)象使用。AOP 代理包含了目標(biāo)對(duì)象的全部方法,但 AOP 代理中的方法與目標(biāo)對(duì)象的方法存在差異:AOP 方法在特定切入點(diǎn)添加了增強(qiáng)處理,并回調(diào)了目標(biāo)對(duì)象的方法。
Spring 的 AOP 代理由 Spring 的 IoC 容器負(fù)責(zé)生成、管理,其依賴關(guān)系也由 IoC 容器負(fù)責(zé)管理。因此,AOP 代理可以直接使用容器中的其他 Bean 實(shí)例作為目標(biāo),這種關(guān)系可由 IoC 容器的依賴注入提供。
aop開(kāi)發(fā)時(shí),其中需要程序員參與的只有 3 個(gè)部分:
- 定義普通業(yè)務(wù)組件。
- 定義切入點(diǎn),一個(gè)切入點(diǎn)可能橫切多個(gè)業(yè)務(wù)組件。
- 定義增強(qiáng)處理,增強(qiáng)處理就是在 AOP 框架為普通業(yè)務(wù)組件織入的處理動(dòng)作。
為了理清關(guān)系,先來(lái)個(gè)類關(guān)系圖。
兩種動(dòng)態(tài)代理方式
Spring默認(rèn)采取的動(dòng)態(tài)代理機(jī)制實(shí)現(xiàn)AOP,當(dāng)動(dòng)態(tài)代理不可用時(shí)(代理類無(wú)接口)會(huì)使用CGlib機(jī)制。
Spring提供了兩種方式來(lái)生成代理對(duì)象: JDKProxy和Cglib,具體使用哪種方式生成由AopProxyFactory根據(jù)AdvisedSupport對(duì)象的配置來(lái)決定。默認(rèn)的策略是如果目標(biāo)類是接口,則使用JDK動(dòng)態(tài)代理技術(shù),否則使用Cglib來(lái)生成代理。
JDK動(dòng)態(tài)代理
- JDK動(dòng)態(tài)代理主要涉及到j(luò)ava.lang.reflect包中的兩個(gè)類:Proxy和InvocationHandler。InvocationHandler是一個(gè)接口,通過(guò)實(shí)現(xiàn)該接口定義橫切邏輯,并通過(guò)反射機(jī)制調(diào)用目標(biāo)類的代碼,動(dòng)態(tài)將橫切邏輯和業(yè)務(wù)邏輯編制在一起。
- Proxy利用InvocationHandler動(dòng)態(tài)創(chuàng)建一個(gè)符合某一接口的實(shí)例,生成目標(biāo)類的代理對(duì)象。
CGLib動(dòng)態(tài)代理
- CGLib全稱為Code Generation Library,是一個(gè)強(qiáng)大的高性能,高質(zhì)量的代碼生成類庫(kù),可以在運(yùn)行期擴(kuò)展Java類與實(shí)現(xiàn)Java接口,CGLib封裝了asm,可以再運(yùn)行期動(dòng)態(tài)生成新的class。和JDK動(dòng)態(tài)代理相比較:JDK創(chuàng)建代理有一個(gè)限制,就是只能為接口創(chuàng)建代理實(shí)例,而對(duì)于沒(méi)有通過(guò)接口定義業(yè)務(wù)方法的類,則可以通過(guò)CGLib創(chuàng)建動(dòng)態(tài)代理。
知識(shí)拓展
通過(guò)上面的分析,大家是否有種熟悉的感覺(jué),似乎和攔截器、過(guò)濾器的功能相似。那么問(wèn)題來(lái)了,aop與攔截器、過(guò)濾器是什么關(guān)系。
先來(lái)回顧一下攔截器與過(guò)濾器。如下圖一網(wǎng)友的測(cè)試,在web.xml中注冊(cè)了TestFilter1和TestFilter2。然后在spring的配置文件中配置了BaseInterceptor和TestInterceptor。得到的結(jié)果如下圖所示。從圖中可以看出,攔截器和過(guò)濾器都橫切了業(yè)務(wù)方法,看似符合aop的思想。
Filter過(guò)濾器:攔截web訪問(wèn)url地址。 Interceptor攔截器:攔截以 .action結(jié)尾的url,攔截Action的訪問(wèn)。 Spring AOP攔截器:只能攔截Spring管理Bean的訪問(wèn)(業(yè)務(wù)層Service)