1.AOP概述
1.1.AOP到底是什么
AOP只適合那些具有橫切面邏輯的應(yīng)用場(chǎng)合,如性能監(jiān)測(cè),訪問(wèn)控制品腹,事務(wù)管理及日志記錄
1.2.AOP術(shù)語(yǔ)
- 連接點(diǎn)(JoinPoint):特定點(diǎn)是程序執(zhí)行某個(gè)特定位置,如初始化開始前啸胧,初始化開始后等等赶站。一個(gè)類或一段程序代碼擁有一些具有邊界性質(zhì)的特定點(diǎn),這些代碼中的特定點(diǎn)就被稱為“連接點(diǎn)”纺念。Spring僅支持方法的連接點(diǎn)贝椿,僅能在方法調(diào)用前、方法調(diào)用后陷谱、方法拋出異常時(shí)及方法調(diào)用前后這些程序執(zhí)行點(diǎn)織入增強(qiáng)烙博。
- 切點(diǎn)(Pointcut):每個(gè)程序類都有多個(gè)連接點(diǎn),就好像每個(gè)類有多個(gè)方法一樣叭首,那么如何定位感興趣的連接點(diǎn)呢?AOP通過(guò)"切點(diǎn)"來(lái)定位特定的連接點(diǎn)习勤。在Spring中,切點(diǎn)通過(guò)Pointcut接口進(jìn)行描述焙格,它使用類和方法作為連接點(diǎn)的查詢條件图毕。
- 增強(qiáng)(Advice):增強(qiáng)是織入目標(biāo)類連接點(diǎn)上的一段程序代碼,換句話說(shuō)增強(qiáng)就是添加到目標(biāo)連接點(diǎn)上的一段程序邏輯眷唉。并且Spring所提供的增強(qiáng)接口都是帶方位名的予颤,如BeforeAdvice等等。只有結(jié)合切點(diǎn)和增強(qiáng)冬阳,才能確定特定的連接點(diǎn)并實(shí)施增強(qiáng)邏輯蛤虐。
- 目標(biāo)對(duì)象(Target):增強(qiáng)邏輯織入目標(biāo)類,如果沒有AOP肝陪,那么目標(biāo)業(yè)務(wù)類要自己實(shí)現(xiàn)所有的邏輯驳庭。在AOP的幫助下,程序只需要實(shí)現(xiàn)非橫切邏輯的代碼部分氯窍,而性能監(jiān)視和事務(wù)管理等這些橫切邏輯則可以使用AOP動(dòng)態(tài)織入特定的連接點(diǎn)上饲常。
- 引介(Introduction):引介是一種特殊的增強(qiáng),它為類添加一些屬性和方法狼讨。這樣贝淤,即使一個(gè)業(yè)務(wù)類原本沒有實(shí)現(xiàn)某個(gè)接口,通過(guò)AOP的引介功能政供,也可以動(dòng)態(tài)地為該業(yè)務(wù)類添加接口的實(shí)現(xiàn)邏輯播聪,讓業(yè)務(wù)類成為這個(gè)接口的實(shí)現(xiàn)類。
- 織入(Weaving):織入是將增強(qiáng)添加到目標(biāo)類的具體連接點(diǎn)上的過(guò)程布隔,將目標(biāo)類离陶、增強(qiáng)或者引介天衣無(wú)縫地編織到一起。AOP的3種織入方式:
- 編譯期織入:要求使用特殊的Java編譯器
- 類裝載期織入:要求使用特殊的類裝載器
- 動(dòng)態(tài)代理織入:在運(yùn)行期為目標(biāo)類添加增強(qiáng)生成子類的方式
- 代理(Proxy):一個(gè)類被AOP織入增強(qiáng)之后衅檀,就產(chǎn)生了一個(gè)結(jié)果類枕磁,它是融合了原類和增強(qiáng)邏輯的代理類。根據(jù)不同的代理方式术吝,代理類既可能是和原類具有相同接口的類计济,也可能就是原類的子類,所以可以采用與調(diào)用原類相同的方式調(diào)用代理類排苍。
- 切面(Aspect):切面由切點(diǎn)和增強(qiáng)(引介)組成沦寂,它既包括橫切邏輯的定義,也包括連接點(diǎn)的定義淘衙。AOP的工作重心在于如何將增強(qiáng)應(yīng)用于目標(biāo)對(duì)象的連接點(diǎn)上传藏,包括以下兩點(diǎn)
- 通過(guò)切點(diǎn)和增強(qiáng)定位到連接點(diǎn)上
- 在增強(qiáng)中編寫切面的代碼
1.3.AOP的實(shí)現(xiàn)者
- AspectJ
- AspectWerkz
- JBossAOP
- SpringAOP
2.基礎(chǔ)知識(shí)
Spring AOP使用動(dòng)態(tài)代理技術(shù)在運(yùn)行時(shí)織入增強(qiáng)代碼,Spring AOP使用兩種代理機(jī)制:一種
基于JDK的動(dòng)態(tài)代理彤守,另一種是基于CGLib的動(dòng)態(tài)代理毯侦。之所以需要兩種代理機(jī)制,是因?yàn)楹艽蟪潭壬螶DK本身只提供接口的代理具垫,而不支持類的代理侈离。
所有實(shí)例的代碼地址
Aop實(shí)例的github地址
2.1.帶有橫切面邏輯的實(shí)例
以下是通過(guò)代碼實(shí)現(xiàn),當(dāng)某個(gè)方法需要進(jìn)行性能監(jiān)視時(shí)筝蚕,必須要調(diào)整方法代碼卦碾,在方法體前后分別添加開啟性能監(jiān)視和結(jié)束性能監(jiān)視的代碼。
public interface ForumService {
public void removeTopic(int topicID);
public void removeForum(int topicID);
}
package com.aop.impl;
public class ForumServiceImpl implements ForumService {
public void removeTopic(int topicID){
//開始對(duì)該方法進(jìn)行性能監(jiān)視
PerformanceMonitor.begin("removeTopic");
System.out.println("模擬刪除論壇topic記錄:"+topicID);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//結(jié)束對(duì)該方法的性能監(jiān)視
PerformanceMonitor.end();
}
public void removeForum(int topicID){
//開始對(duì)該方法進(jìn)行性能監(jiān)視
PerformanceMonitor.begin("removeForum");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("模擬刪除Forum記錄:"+topicID);
//結(jié)束對(duì)該方法的性能監(jiān)視
PerformanceMonitor.end();
}
}
public class MethodPerformance {
private long begin;
private long end;
private String serviceMethod;
public MethodPerformance(String serviceMethod) {
this.serviceMethod = serviceMethod;
this.begin = System.currentTimeMillis();//記錄目標(biāo)類方法開始執(zhí)行點(diǎn)的開始時(shí)間
}
public void printPerformance() {
end = System.currentTimeMillis();//獲取目標(biāo)類方法執(zhí)行完成后的系統(tǒng)時(shí)間起宽,進(jìn)而計(jì)算出目標(biāo)類方法的執(zhí)行時(shí)間
long elapse = end - begin;
System.out.println(serviceMethod+"花費(fèi)"+elapse+"毫秒");
}
}
public class PerformanceMonitor {
//通過(guò)一個(gè)ThreadLocal保存與調(diào)用線程相關(guān)的性能監(jiān)視信息
private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<>();
//啟動(dòng)對(duì)某一目標(biāo)方法的性能監(jiān)視
public static void begin(String method){
System.out.println("begin monitor....");
MethodPerformance mp=new MethodPerformance(method);
performanceRecord.set(mp);
}
public static void end(){
System.out.println("end monitor");
MethodPerformance mp=performanceRecord.get();
//打印出方法性能監(jiān)視的結(jié)果信息
mp.printPerformance();
}
}
public class TestForumService {
public static void main(String[] args) {
ForumService forumService=new ForumServiceImpl();
forumService.removeForum(10);
forumService.removeTopic(20);
}
}
2.2.JDK動(dòng)態(tài)代理
public class PerformanceHandler implements InvocationHandler {
private Object target;
public PerformanceHandler(Object target) {//target為目標(biāo)業(yè)務(wù)類
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
PerformanceMonitor.begin(target.getClass().getName()+"."+method.getName());
Object obj=method.invoke(target,args);//通過(guò)反射調(diào)用業(yè)務(wù)類的目標(biāo)方法
PerformanceMonitor.end();
return obj;
}
}
public class TestForumService {
public static void main(String[] args) {
ForumService target = new ForumServiceImpl();
PerformanceHandler handler = new PerformanceHandler(target);
ForumService proxy= (ForumService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);
proxy.removeForum(10);
proxy.removeTopic(1020);
}
}
2.3.CGLib動(dòng)態(tài)代理
由于CGLib采用動(dòng)態(tài)創(chuàng)建子類的方式生成代理對(duì)象洲胖,所以不能對(duì)目標(biāo)類中的final或private方法進(jìn)行代理
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz);//設(shè)置需要?jiǎng)?chuàng)建子類的類
enhancer.setCallback(this);
return enhancer.create();//通過(guò)字節(jié)碼技術(shù)動(dòng)態(tài)創(chuàng)建子類實(shí)例
}
@Override
public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//攔截父類所有方法
PerformanceMonitor.begin(obj.getClass().getName()+"."+method.getName());
Object result=methodProxy.invokeSuper(obj,objects);//通過(guò)反射調(diào)用業(yè)務(wù)類的目標(biāo)方法
PerformanceMonitor.end();
return result;
}
}
public class TestForumService {
public static void main(String[] args) {
CGLibProxy proxy=new CGLibProxy();
ForumServiceImpl forumService= (ForumServiceImpl) proxy.getProxy(ForumServiceImpl.class);//通過(guò)動(dòng)態(tài)生成子類的方式創(chuàng)建 代理類
forumService.removeTopic(10);
forumService.removeForum(1024);
}
}
2.4.AOP聯(lián)盟
2.5.代理知識(shí)小結(jié)
雖然通過(guò)上面的PerformanceHandler或CGLibProxy實(shí)現(xiàn)了性能監(jiān)視橫切邏輯的動(dòng)態(tài)織入,但是這種實(shí)現(xiàn)方式存在3個(gè)明顯需要改進(jìn)的地方
- 目標(biāo)類的所有方法都添加了性能監(jiān)視橫切邏輯坯沪,而有時(shí)這并不是我們所期望的绿映,我們可能只希望對(duì)業(yè)務(wù)中的某些特定方法添加橫切面邏輯
- 通過(guò)硬編碼的方式指定了織入橫切邏輯的織入點(diǎn),即在目標(biāo)類業(yè)務(wù)方法的開始和結(jié)束前織入代碼
- 手工編寫代理實(shí)例的創(chuàng)建過(guò)程腐晾,在為不同的類創(chuàng)建代理時(shí)叉弦,需要分別編寫相應(yīng)的創(chuàng)建代碼,無(wú)法做到通用
JDK動(dòng)態(tài)代理所創(chuàng)建的代理對(duì)象赴魁,在Java 1.3下卸奉,性能差強(qiáng)人意,研究表明CGLib所創(chuàng)
建的動(dòng)態(tài)代理對(duì)象的性能依舊比JDK所創(chuàng)建的動(dòng)態(tài)代理對(duì)象的性能高不少(大概10倍)颖御,但是CGLib在創(chuàng)建代理對(duì)象時(shí)所花費(fèi)的時(shí)間卻比JDK動(dòng)態(tài)代理多(大概8倍)榄棵。對(duì)于singleton的代理對(duì)象或者具有實(shí)例池的代理,因?yàn)闊o(wú)需頻繁的創(chuàng)建代理對(duì)象潘拱,所以比較適合采用CGLib動(dòng)態(tài)代理技術(shù)疹鳄,反之則適合采用JDK動(dòng)態(tài)代理技術(shù)
3.創(chuàng)建增強(qiáng)類
Spring使用增強(qiáng)類定義了橫切面邏輯,同時(shí)由于Spring只支持方法連接點(diǎn)芦岂,增強(qiáng)還包括方法在哪一點(diǎn)加入橫切代碼的方位信息瘪弓,所以增強(qiáng)既包含橫切邏輯,又包含部分連接點(diǎn)的信息
3.1.增強(qiáng)類型
- 前置增強(qiáng):在目標(biāo)方法前實(shí)施增強(qiáng)
- 后置增強(qiáng):在目標(biāo)方法后實(shí)施增強(qiáng)
- 環(huán)繞增強(qiáng):在目標(biāo)方法前后實(shí)施增強(qiáng)
- 異常拋出增強(qiáng):表示在目標(biāo)方法拋出異常后實(shí)施增強(qiáng)
- 引介增強(qiáng):在目標(biāo)類中添加一些新的屬性和方法
3.2.前置增強(qiáng)
通過(guò)實(shí)現(xiàn)MethodBeforeAdvice接口
public interface Waiter {
public void greetTo(String name);
public void serveTo(String name);
}
public class NativeWaiter implements Waiter {
@Override
public void greetTo(String name) {
System.out.println("greet to "+name+"...");
}
@Override
public void serveTo(String name) {
System.out.println("serving to "+name+"...");
}
}
public class GreetingBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object o) throws Throwable {//在目標(biāo)方法調(diào)用前使用
String clientName= (String) args[0];
System.out.println("How are you! Mr."+clientName);
}
}
public class BeforeAdviceTest {
@Test
public void before() {
System.out.println("測(cè)試代碼");
Waiter target = new NativeWaiter();
BeforeAdvice advice=new GreetingBeforeAdvice();
//spring提供代理工廠
ProxyFactory pf=new ProxyFactory();
//設(shè)置代理目標(biāo)
pf.setTarget(target);
//為代理目標(biāo)添加增強(qiáng)
pf.addAdvice(advice);
//生成代理實(shí)例
Waiter proxy= (Waiter) pf.getProxy();
proxy.greetTo("John");
proxy.serveTo("Tom");
}
}
上面代碼中使用ProxyFactory代理禽最,ProxyFactory內(nèi)部使用的就是JDK或者是CGLib動(dòng)態(tài)代理技術(shù)將增強(qiáng)應(yīng)用到目標(biāo)類中的腺怯。如果通過(guò)ProxyFactory的setInTerfaces方法指定目標(biāo)接口進(jìn)行代理袱饭,則使用JDK代理,如果是針對(duì)類代理呛占,則使用CGLib代理虑乖。此外還可以通過(guò)setOptimize方法讓ProxyFactory啟動(dòng)優(yōu)化代理方式。
3.3.后置增強(qiáng)
后置增強(qiáng)和前置增強(qiáng)的代碼類似晾虑,前置增強(qiáng)要實(shí)現(xiàn)接口MethodBeforeAdvice疹味,后置增強(qiáng)則要實(shí)現(xiàn)接口AfterReturningAdvice
public class GreetingAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("enjoy yourself!");
}
}
3.4.環(huán)繞增強(qiáng)
環(huán)繞增強(qiáng)綜合實(shí)現(xiàn)了前置、后置增強(qiáng)的功能
public class GreetingInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {//截獲目標(biāo)類方法的執(zhí)行帜篇,并在前后添加橫切邏輯
Object[] args = methodInvocation.getArguments();
String clientName = (String) args[0];
System.out.println("How are you !Mr."+clientName+".");//在目標(biāo)方法前執(zhí)行
Object obj=methodInvocation.proceed();//通過(guò)反射機(jī)制調(diào)用目標(biāo)方法
System.out.println("Please enjoy yourself糙捺!");
return obj;
}
}
3.5.異常拋出異常
異常拋出異常最適合的應(yīng)用場(chǎng)景是事務(wù)管理,當(dāng)參與事務(wù)的某個(gè)DAO發(fā)生異常時(shí)笙隙,事務(wù)管理器就必須回滾事務(wù)
ThrowAdvice異常拋出增強(qiáng)接口沒有定義任何方法洪灯,它是一個(gè)標(biāo)簽接口,在運(yùn)行期Spring使用反射機(jī)制自行判斷逃沿,必須采用以下簽名形式定義異常拋出的增強(qiáng)方法婴渡。
public class TransactionManager implements ThrowsAdvice {
//定義增強(qiáng)邏輯
public void afterThrowing(Method method,Object[] args,Object target,Exception ex) throws Throwable{
System.out.println("--------------");
System.out.println("method:"+method.getName());
System.out.println("拋出異常:"+ex.getMessage());
System.out.println("成功回滾事務(wù)!");
}
}
3.6.引介增強(qiáng)
引介增強(qiáng)是為目標(biāo)類創(chuàng)建新的方法和屬性凯亮,所以引介增強(qiáng)的連接點(diǎn)是類級(jí)別的边臼,而非方法級(jí)別的,通過(guò)引介類增強(qiáng)假消,可以為目標(biāo)類添加一個(gè)接口的實(shí)現(xiàn)柠并,即原來(lái)目標(biāo)類未實(shí)現(xiàn)某個(gè)方法接口,通過(guò)引介增強(qiáng)可以為目標(biāo)類創(chuàng)建實(shí)現(xiàn)某接口的代理富拗。
public class ControllablePerformanceMonitor extends DelegatingIntroductionInterceptor implements Monitorable {
private ThreadLocal<Boolean> MonitorStatusMap = new ThreadLocal<>();
@Override
public void setMonitorActive(boolean active) {
MonitorStatusMap.set(active);
}
//攔截方法
public Object invoke(MethodInvocation mi) throws Throwable {
Object obj = null;
//對(duì)于性能監(jiān)視可控代理臼予,通過(guò)判斷其狀態(tài)決定是否開啟性能監(jiān)控功能
if(MonitorStatusMap.get()!=null&&MonitorStatusMap.get()){
PerformanceMonitor.begin(mi.getClass().getName()+"."+mi.getMethod().getName());
obj=super.invoke(mi);
PerformanceMonitor.end();
}else {
obj=super.invoke(mi);
}
return obj;
}
}
4.創(chuàng)建切面
增強(qiáng)被織入目標(biāo)類的所有方法中,假設(shè)我們希望有選擇地織入目標(biāo)類的某些特定的方法中啃沪,就需要使用切點(diǎn)進(jìn)行目標(biāo)連接點(diǎn)的定位粘拾。
Spring通過(guò)Pointcut接口來(lái)描述切點(diǎn),Pointcut由ClassFilter和MethodMatcher構(gòu)成创千,它通過(guò)ClassFilter定位到某些特定類上缰雇,通過(guò)MethodMatcher定位到某些特定方法上,這樣Pointcut就擁有了描述某些類的某些特定方法的能力追驴。
4.1.切點(diǎn)類型
- 靜態(tài)方法切點(diǎn):匹配所有的類
- 動(dòng)態(tài)方法切點(diǎn):匹配所有的類
- 注解切點(diǎn):表示注解切點(diǎn)
- 表達(dá)式切點(diǎn):支持AspectJ切點(diǎn)表達(dá)式語(yǔ)法而定義的接口
- 流程切點(diǎn):根據(jù)程序執(zhí)行堆棧的信息查看目標(biāo)方法是否由某一個(gè)方法直接或間接發(fā)起調(diào)用械哟,以此判斷是否為匹配的連接點(diǎn)
- 復(fù)合切點(diǎn):使用鏈接表達(dá)式對(duì)切點(diǎn)進(jìn)行操作
4.2.切面類型
- Advisor:代表一般切面,僅包含一個(gè)Advice
- PointcutAdvisor:代表具有切點(diǎn)的切面殿雪,包含Advice和Pointcut兩個(gè)類
- IntroductionAdvisor:代表引介切面
4.3.靜態(tài)普通方法名匹配切面
public class GreetingBeforeAdvisor extends StaticMethodMatcherPointcutAdvisor {
@Override
public boolean matches(Method method, Class<?> clazz) {
return "greetTo".equals(method.getName());
}
public ClassFilter getClassFilter(){
return new ClassFilter() {
@Override
public boolean matches(Class<?> clazz) {
return Waiter.class.isAssignableFrom(clazz);
}
};
}
}
4.4.靜態(tài)正則表達(dá)式方法切面
<!--靜態(tài)正則表達(dá)式方法匹配切面-->
<bean id="regexpAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
p:advice-ref="greetingBefore">
<property name="patterns">
<list>
<value>.*greet.*</value><!--定義可匹配模式串暇咆,“.*greet.*”-->
</list>
</property>
</bean>
<bean id="waiter1"class="org.springframework.aop.framework.ProxyFactoryBean"
p:proxyTargetClass="true"
p:interceptorNames="regexpAdvisor"
p:target-ref="waiterTarget"
/>
4.5.動(dòng)態(tài)切面
Spring會(huì)在創(chuàng)建動(dòng)態(tài)代理織入切面時(shí),對(duì)目標(biāo)類中所有方法進(jìn)行靜態(tài)切點(diǎn)檢查;在生成織入切面的代理對(duì)象后爸业,第一次調(diào)用代理類的每一個(gè)方法時(shí)都會(huì)進(jìn)行一次靜態(tài)切點(diǎn)檢查其骄,如果本次檢查就能從候選者列表中將該方法排除,則以后對(duì)該方法的調(diào)用就不再執(zhí)行靜態(tài)切點(diǎn)檢查沃呢;對(duì)于那些在靜態(tài)切點(diǎn)檢查時(shí)匹配的方法年栓,在后續(xù)調(diào)用該方法時(shí),將執(zhí)行動(dòng)態(tài)切點(diǎn)檢查
public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut {
private static List<String> specialClientList=new ArrayList<>();
static{
specialClientList.add("Jhon");
specialClientList.add("Tom");
}
public ClassFilter getClassFilter(){
return new ClassFilter() {
@Override
public boolean matches(Class<?> clazz) {
System.out.println("調(diào)用getClassFilter()對(duì)"+clazz.getName()+"靜態(tài)檢查.");
return Waiter.class.isAssignableFrom(clazz);
}
};
}
@Override
public boolean matches(Method method, Class<?> clazz, Object... objects) {//對(duì)方法進(jìn)行動(dòng)態(tài)切點(diǎn)檢查
System.out.println("調(diào)用動(dòng)態(tài)檢查方法對(duì)"+clazz.getName()+"類的"+method+"動(dòng)態(tài)檢查.");
String clientName= (String) objects[0];
return specialClientList.contains(clientName);
}
@Override
public boolean matches(Method method, Class<?> clazz) {//對(duì)方法進(jìn)行靜態(tài)切點(diǎn)檢查
System.out.println("調(diào)用靜態(tài)檢查方法對(duì)"+clazz.getName()+"類的"+method+"靜態(tài)檢查.");
return "greetTo".equals(method.getName());
}
}
<!--動(dòng)態(tài)切面-->
<bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="pointcut">
<bean class="cn.com.dynamicPoint.GreetingDynamicPointcut"/>
</property>
<property name="advice">
<bean class="cn.com.beforeEnhancer.GreetingBeforeAdvice"/>
</property>
</bean>
<bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="dynamicAdvisor"
p:target-ref="waiterTarget"
p:proxyTargetClass="true"
/>
每次調(diào)用代理對(duì)象的任何一個(gè)方法薄霜,都會(huì)執(zhí)行動(dòng)態(tài)切點(diǎn)檢查,這將導(dǎo)致很大的性能問(wèn)題纸兔,所以惰瓜,在定義動(dòng)態(tài)切點(diǎn)時(shí),切勿忘記同時(shí)覆蓋getClassFilter()matches(Metho method汉矿,Class clazz)方法崎坊,通過(guò)靜態(tài)切點(diǎn)檢查排除大部分方法
4.6.流程切面
流程切點(diǎn)代表由某個(gè)方法直接或間接發(fā)起調(diào)用的其他方法。流程切面和動(dòng)態(tài)切面從某種程度上來(lái)說(shuō)可以算是一類切面洲拇,因?yàn)槎叨夹枰谶\(yùn)行期判斷動(dòng)態(tài)的環(huán)境奈揍。對(duì)于流程切面來(lái)說(shuō),代理對(duì)象在每次調(diào)用目標(biāo)類方法時(shí)赋续,都需要判斷方法調(diào)用堆棧中是否有滿足流程切點(diǎn)要求的方法男翰,因此,和動(dòng)態(tài)切面一樣纽乱,流程切面對(duì)性能的影響也很大蛾绎。
public class WaiterDelegate {
private Waiter waiter;
public void service(String clientName){//waiter的方法通過(guò)該方法發(fā)起調(diào)用
waiter.serveTo(clientName);
waiter.greetTo(clientName);
}
public void setWaiter(Waiter waiter) {
this.waiter = waiter;
}
}
<!--流程切面-->
<bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
<constructor-arg type="java.lang.Class">
<value>cn.com.flowPoint.WaiterDelegate</value>
</constructor-arg>
<constructor-arg type="java.lang.String">
<value>service</value>
</constructor-arg>
</bean>
<bean id="controlFlowAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut-ref="controlFlowPointcut"
p:advice-ref="greetingBefore"
/>
<bean id="waiter3" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="controlFlowAdvisor"
p:target-ref="waiterTarget"
p:proxyTargetClass="true"
/>
4.7.符合切點(diǎn)切面
有時(shí),一個(gè)切點(diǎn)可能難以描述目標(biāo)連接點(diǎn)的信息鸦列,比如在前面的例子中租冠,假設(shè)我們希望WaiterDelegate#service發(fā)起調(diào)用且被調(diào)用的方法是Waiter#greetTo時(shí)才織入增強(qiáng),這個(gè)切點(diǎn)就是符合切點(diǎn)薯嗤。
public class GreetingComposablePointcut {
public Pointcut getInTersectionPointcut(){
ComposablePointcut cp=new ComposablePointcut();//創(chuàng)建一個(gè)復(fù)合切點(diǎn)
Pointcut pt1=new ControlFlowPointcut(WaiterDelegate.class,"service");//創(chuàng)建一個(gè)流程切點(diǎn)
NameMatchMethodPointcut pt2=new NameMatchMethodPointcut();//創(chuàng)建方法名切點(diǎn)
pt2.addMethodName("greetTo");
return cp.intersection(pt1).intersection((Pointcut) pt2);
}
}
<!--復(fù)合切點(diǎn)切面-->
<bean id="gcp" class="cn.com.composablePointcut.GreetingComposablePointcut"></bean>
<bean id="composableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
p:pointcut="#{gcp.inTersectionPointcut}"
p:advice-ref="greetingBefore"
/>
<!--引用gcp.inTersectionPointcut方法返回的復(fù)合切點(diǎn)-->
<bean id="waiter4" class="org.springframework.aop.framework.ProxyFactoryBean"
p:interceptorNames="composableAdvisor"
p:target-ref="waiterTarget"
p:proxyTargetClass="true"
/><!--使用復(fù)合切點(diǎn)-->
4.8.引介切面
5.自動(dòng)創(chuàng)建代理
在前面的例子中顽爹,都通過(guò)ProxyFactoryBean創(chuàng)建織入切面的代理,每個(gè)需要被代理的Bean都需要使用一個(gè)ProxyFactoryBean進(jìn)行配置骆姐,雖然可以使用父子<bean>進(jìn)行改造镜粤,但還是很麻煩,Spring提供了自動(dòng)代理機(jī)制诲锹,讓容器自動(dòng)生成代理繁仁,可以從繁瑣的配置中解放出來(lái)。Spring使用BeanPostProcessor自動(dòng)完成這項(xiàng)工作
5.1.實(shí)現(xiàn)類介紹
這些基于BeanPostProcessor的自動(dòng)代理創(chuàng)建器的實(shí)現(xiàn)類归园,將根據(jù)一些規(guī)則自動(dòng)在容器實(shí)例化Bean時(shí)為匹配的Bean生成自動(dòng)代理實(shí)例黄虱。
- 基于Bean配置名規(guī)則的自動(dòng)代理創(chuàng)建器:允許為一組特定配置名的Bean自動(dòng)創(chuàng)建代理實(shí)例的代理創(chuàng)建器,實(shí)現(xiàn)類為BeanNameAutoProxyCreator
- 基于Advisor匹配機(jī)制的自動(dòng)代理創(chuàng)建器:它會(huì)對(duì)容器中所有的Advisor進(jìn)行掃描庸诱,自動(dòng)將這些切面應(yīng)用到匹配的Bean中(為目標(biāo)Bean創(chuàng)建帶實(shí)例)捻浦,實(shí)現(xiàn)類為DefaultAdvisorAutoProxyCreator
- 基于Bean中AspectJ注解標(biāo)簽的自動(dòng)代理創(chuàng)建器:為包含AspectJ注解的Bean自動(dòng)創(chuàng)建代理實(shí)例晤揣,實(shí)現(xiàn)類為AnnotationAwareAspectJAutoProxyCreator
5.2.BeanNameAutoProxyCreator
<!--自動(dòng)代理BeanNameAutoProxyCreator-->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
p:beanNames="*er"
p:interceptorNames="greetingBefore"
p:optimize="true"
/>
5.3.DefaultAdvisorAutoProxyCreator
DefaultAdvisorAutoProxyCreator能夠掃描容器中的Advisor,并將Advisor自動(dòng)織入匹配的目標(biāo)Bean中朱灿,即為匹配的目標(biāo)Bean自動(dòng)創(chuàng)建代理
<bean id="regexpAdvisor1" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
p:patterns=".*greet.*"
p:advice-ref="greetingBefore"
/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
5.4.AOP無(wú)法增強(qiáng)疑難問(wèn)題剖析
因?yàn)锳OP底層的實(shí)現(xiàn)原理基于JDK和CGLib動(dòng)態(tài)代理昧识,在JDK動(dòng)態(tài)代理中通過(guò)接口來(lái)實(shí)現(xiàn)方法攔截,所以必須保證要攔截的目標(biāo)方法在接口中有定義盗扒,在CGLib動(dòng)態(tài)代理中通過(guò)動(dòng)態(tài)生成代理子類來(lái)實(shí)現(xiàn)方法攔截跪楞,所以必須要確保攔截的目標(biāo)方法可被子類訪問(wèn),也就是目標(biāo)方法必須定義為非final侣灶,即非私有實(shí)例方法
<bean id="regexpAdvisor1" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
p:pattern=".*To.*"
p:advice-ref="greetingBefore"
/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
<!--修改一下自動(dòng)代理的正則表達(dá)式甸祭,所有方法中包含To的都要織入增強(qiáng),-->
public class NativeWaiter implements Waiter {
@Override
public void greetTo(String name) {
//throw new RuntimeException("運(yùn)行異常");
System.out.println("greet to "+name+"...");
}
@Override
public void serveTo(String name) {
greetTo(name);
System.out.println("serving to "+name+"...");
}
//只調(diào)用serveTo方法褥影,serverTo和greetTo方法能否可以同時(shí)增強(qiáng)
/**
console打印的日志如下
How are you! Mr.Jhon
greet to Jhon...
serving to Jhon...
表明被調(diào)用的方法沒有織入增強(qiáng)
**/
}
>在調(diào)用內(nèi)部方法時(shí)池户,讓其通過(guò)代理類調(diào)用內(nèi)部的方法來(lái)解決內(nèi)部方法不能被代理的情況,因此凡怎,需要讓原來(lái)的Waiter實(shí)現(xiàn)一個(gè)可注入自身代理類的接口
注:通過(guò)配置代理類來(lái)調(diào)用內(nèi)部方法還是不能夠給方法織入增強(qiáng)校焦,關(guān)于這點(diǎn)如果有大神知道,請(qǐng)告知统倒,我后面也會(huì)持續(xù)研究這個(gè)問(wèn)題寨典,如果有進(jìn)展會(huì)第一時(shí)間更新文章