1. 職責(zé)鏈模式(Chain Of Responsibility Design Pattern)
在職責(zé)鏈模式中免猾,多個處理器(也就是剛剛定義中說的“接收對象”)依次處理同一個請求宏榕。一個請求先經(jīng)過 A 處理器處理延蟹,然后再把請求傳遞給 B 處理器榜轿,B 處理器處理完后再傳遞給 C 處理器疼约,以此類推辞州,形成一個鏈條画畅。鏈條上的每個處理器各自承擔(dān)各自的處理職責(zé)砸琅,所以叫作職責(zé)鏈模式。
IHandler是所有處理器類的接口轴踱,handle()方法是抽象方法症脂,每個具體的處理器類(HandlerA、HandlerB)的handle()方法用來處理請求淫僻,HandlerChain是處理器鏈诱篷,代碼如下所示:
public interface IHandler {
boolean handle();
}
public class HandlerA implements IHandler {
@Override
public boolean handle() {
boolean handled = false;
//...
return handled;
}
}
public class HandlerB implements IHandler {
@Override
public boolean handle() {
boolean handled = false;
//...
return handled;
}
}
public class HandlerChain {
private List<IHandler> handlers = new ArrayList<>();
public void addHandler(IHandler handler) {
this.handlers.add(handler);
}
public void handle() {
for (IHandler handler : handlers) {
boolean handled = handler.handle();
if (handled) {
break;
}
}
}
}
// 使用舉例
public class Application {
public static void main(String[] args) {
HandlerChain chain = new HandlerChain();
chain.addHandler(new HandlerA());
chain.addHandler(new HandlerB());
chain.handle();
}
}
2. Spring AOP的基本使用和源碼解析
Spring AOP的基本使用請參考:SpringAOP
Spring AOP源碼解析請參考:Spring AOP源碼淺析
3.Spring AOP的職責(zé)鏈模式
3.1. MethodInteceptor接口
相當(dāng)于上文中的IHandler,每個處理器都要實現(xiàn)該接口雳灵,其中invoke()方法棕所,是處理器的處理邏輯
public interface MethodInteceptor {
Object invoke(MethodInvocation mi) throws Throwable;
}
3.2. CglibMethodInvocation
CglibMethodInvocation相當(dāng)于上文中的HandlerChain ,該類中定義了攔截器鏈以及攔截器鏈的執(zhí)行邏輯细办,代碼模擬了Spring AOP的處理
public class CglibMethodInvocation implements MethodInvocation{
List<MethodInteceptor> inteceptors;
//當(dāng)前攔截器鏈執(zhí)行的位置橙凳。
int currentInterceptorIndex = -1;
//要切入的目標(biāo)類對象蕾殴,方法,方法參數(shù)
Object target;
Method method;
Object[] args;
CglibMethodInvocation(List<MethodInteceptor> inteceptors, Object target, Method method,Object[] args){
this.inteceptors = inteceptors;
this.target = target;
this.method = method;
this.args = args;
}
@Override
public Object proceed() throws Throwable {
//當(dāng)攔截器鏈執(zhí)行完之后岛啸,就執(zhí)行目標(biāo)類的目標(biāo)方法钓觉。
if (this.currentInterceptorIndex == this.inteceptors.size() - 1) {
return method.invoke(target,args);
}
MethodInteceptor inteceptor = this.inteceptors.get(++currentInterceptorIndex);
return inteceptor.invoke(this);
}
}
3.3. MethodInvocation
MethodInvocation是CglibMethodInvocation的抽象。
public interface MethodInvocation {
Object proceed() throws Throwable;
}
3.4. MethodInteceptor的實現(xiàn)類
MethodInteceptor的實現(xiàn)類就是攔截器的具體實現(xiàn)坚踩,在每個invoke(MethodInvocation mi)方法中荡灾,都會調(diào)用mi.proceed()方法,讓攔截器鏈能夠繼續(xù)往下執(zhí)行瞬铸。
public class MethodBeforeAdviceInterceptor implements MethodInteceptor{
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("假裝這里執(zhí)行了切面類@Before標(biāo)注的方法批幌。。嗓节。荧缘。。拦宣。截粗。");
return mi.proceed();
}
}
public class AspectJAfterAdvice implements MethodInteceptor{
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try{
return mi.proceed();
}
finally {
System.out.println("假裝這里執(zhí)行了切面類@After標(biāo)注的方法。鸵隧。绸罗。。豆瘫。珊蟀。");
}
}
}
public class AfterReturningAdviceInterceptor implements MethodInteceptor{
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object returnVal = mi.proceed();
System.out.println("假裝這里執(zhí)行了切面類@AfterReturning標(biāo)注的方法。外驱。育灸。。略步。描扯。");
return returnVal;
}
}
public class AspectJAfterThrowingAdvice implements MethodInteceptor{
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try{
return mi.proceed();
}catch (Throwable throwable){
System.out.println("假裝執(zhí)行了切面類@AfterThrowing標(biāo)注的方法");
throw throwable;
}
}
}
3.5. TargetClass目標(biāo)類
TargetClass目標(biāo)類就是簡單模擬一下定页,Spring AOP中要被切入的類趟薄。
public class TargetClass {
public void targetMethod(String s){
//先簡單拋個異常
//int i = 1 / 0;
System.out.println("假裝這里執(zhí)行了目標(biāo)類的目標(biāo)方法,傳參:"+s);
}
}
3.6. 測試類MainClass
public class MainClass {
public static void main(String[] args) throws Throwable {
//初始化攔截器
List<MethodInteceptor> inteceptors = new ArrayList<>(4);
inteceptors.add(new AspectJAfterThrowingAdvice());
inteceptors.add(new AfterReturningAdviceInterceptor());
inteceptors.add(new AspectJAfterAdvice());
inteceptors.add(new MethodBeforeAdviceInterceptor());
//利用反射獲取目標(biāo)類以及目標(biāo)方法,然后初始化參數(shù)
TargetClass targetClass = new TargetClass();
Method method = targetClass.getClass().getMethod("targetMethod",String.class);
Object[] methodArgs = {"我是參數(shù)"};
//執(zhí)行職責(zé)鏈
CglibMethodInvocation methodInvocation = new CglibMethodInvocation(inteceptors,targetClass,method,methodArgs);
methodInvocation.proceed();
}
}
3.7. 測試結(jié)果
先注掉TargetClass中的int i = 1 / 0:
假裝這里執(zhí)行了切面類@Before標(biāo)注的方法典徊。杭煎。。卒落。羡铲。。儡毕。
假裝這里執(zhí)行了目標(biāo)類的目標(biāo)方法,傳參:我是參數(shù)
假裝這里執(zhí)行了切面類@After標(biāo)注的方法也切。扑媚。。雷恃。疆股。。
假裝這里執(zhí)行了切面類@AfterReturning標(biāo)注的方法倒槐。旬痹。。讨越。两残。。
Process finished with exit code 0
放開注釋把跨,讓目標(biāo)方法拋個異常:
假裝這里執(zhí)行了切面類@Before標(biāo)注的方法人弓。。着逐。票从。。滨嘱。峰鄙。
假裝這里執(zhí)行了切面類@After標(biāo)注的方法。太雨。吟榴。。囊扳。吩翻。
假裝執(zhí)行了切面類@AfterThrowing標(biāo)注的方法
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.ljessie.designpattern.behavior.chain.CglibMethodInvocation.proceed(CglibMethodInvocation.java:25)
at com.ljessie.designpattern.behavior.chain.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:7)
at com.ljessie.designpattern.behavior.chain.CglibMethodInvocation.proceed(CglibMethodInvocation.java:30)
at com.ljessie.designpattern.behavior.chain.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:7)
at com.ljessie.designpattern.behavior.chain.CglibMethodInvocation.proceed(CglibMethodInvocation.java:30)
at com.ljessie.designpattern.behavior.chain.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:6)
at com.ljessie.designpattern.behavior.chain.CglibMethodInvocation.proceed(CglibMethodInvocation.java:30)
at com.ljessie.designpattern.behavior.chain.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:7)
at com.ljessie.designpattern.behavior.chain.CglibMethodInvocation.proceed(CglibMethodInvocation.java:30)
at com.ljessie.designpattern.behavior.chain.MainClass.main(MainClass.java:23)
Caused by: java.lang.ArithmeticException: / by zero
at com.ljessie.designpattern.behavior.chain.TargetClass.targetMethod(TargetClass.java:9)
... 14 more
Process finished with exit code 1
到這里,攔截器鏈的執(zhí)行邏輯與Spring AOP基本相符锥咸。在目標(biāo)方法沒有拋出異常時狭瞎,會走AfterReturning方法;拋出異常時搏予,會走AfterThrowing方法熊锭。
4. 總結(jié)
本文從Spring AOP的源碼中,抽取出來職責(zé)鏈模式相關(guān)代碼雪侥,簡單介紹了下Spring AOP中職責(zé)鏈模式的應(yīng)用碗殷。
在Spring AOP中,MethodInterceptor是所有攔截器的抽象接口速缨,然后各個攔截器實現(xiàn)了MethodInterceptor接口锌妻。
CglibMethodInvocation是中的proceed()方法,定義了攔截器鏈的處理邏輯旬牲,當(dāng)所有攔截器都執(zhí)行完畢之后仿粹,開始執(zhí)行目標(biāo)類的目標(biāo)方法搁吓。MethodInvocation接口是CglibMethodInvocation的抽象。
如有分析不當(dāng)之處吭历,還煩請各位看官多多指正擎浴。