前面我們講 JDK 動態(tài)代理和 CGLIB 動態(tài)代理時,都只說了一次代理,即對目標(biāo)方法做一次增強操作鸵赫。
下面我們來看看如何用 JDK 動態(tài)代理如何實現(xiàn)多重代理。
嵌套代理對象
本文中的代碼用到 Lombok 注解躏升,因此需要引入下面的 Maven 依賴辩棒。
<!--
Lombok能通過注解的方式,在編譯時自動為屬性生成構(gòu)造器膨疏、getter/setter一睁、equals、hashcode佃却、toString 等方法
如果需要使用者吁,還需要在 IDE 中安裝 lombok 插件,安裝步驟請百度
-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>
首先饲帅,我們來修改一下 Spring AOP (二) JDK 動態(tài)代理 中提到的 JdkDynamicProxy
類复凳。
@Data
public class JdkDynamicProxy1 implements InvocationHandler {
/**
* 目標(biāo)對象(也被稱為被代理對象)
*
* Java 代理模式的一個必要要素就是代理對象要能拿到被代理對象的引用
*/
private Object target;
/**
* 用來標(biāo)識 InvocationHandler invoke 調(diào)用
*/
private String tag;
public JdkDynamicProxy1(Object target){
this.target=target;
}
/**
* 回調(diào)方法
* @param proxy JDK 生成的代理對象
* @param method 被代理的方法(也就是需要增強的方法)
* @param args 被代理方法的參數(shù)
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 直接輸出 hashCode 值,避免 invoke 遞歸調(diào)用灶泵,導(dǎo)致棧溢出
if (method.getName().equals("hashCode")) {
return hashCode();
}
System.out.println("JdkDynamicProxy1 invoke 方法執(zhí)行前---------------" + info(proxy));
Object object= method.invoke(this.target, args);
System.out.println("JdkDynamicProxy1 invoke 方法執(zhí)行后----------------" + info(proxy));
return object;
}
/**
* 輸出代理類的一些信息育八,比如類名,hashCode 等
* @param proxy
* @return
*/
private String info(Object proxy) {
return proxy.getClass().getName() + ":" + proxy.hashCode() + "----------------" + this.tag;
}
/**
* 獲取被代理接口實例對象
*
* 通過 Proxy.newProxyInstance 可以獲得一個代理對象赦邻,它實現(xiàn)了 target.getClass().getInterfaces() 接口
*
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
}
JdkDynamicProxy1
類中新增了 tag
字段髓棋,用于標(biāo)識 invoke
方法調(diào)用中的代理類,并在 invoke
方法調(diào)用中打印了代理類的類名和 hashCode
值深纲。
Client1
類中的測試代碼仲锄,通過多個代理對象嵌套的方式實現(xiàn)了多重代理。
public class Client1 {
public static void main(String[] args) {
// 1. 構(gòu)造目標(biāo)對象
Cat catTarget = new Cat();
// 2. 根據(jù)目標(biāo)對象生成代理對象
JdkDynamicProxy1 proxy = new JdkDynamicProxy1(catTarget);
proxy.setTag("第一個代理類");
// JDK 動態(tài)代理是基于接口的湃鹊,所以只能轉(zhuǎn)換為 Cat 實現(xiàn)的接口 Animal
Animal catProxy = proxy.getProxy();
// 3. 根據(jù)第一個代理對象生成代理對象
JdkDynamicProxy1 proxy2 = new JdkDynamicProxy1(catProxy);
proxy2.setTag("第二個代理類");
// JDK 動態(tài)代理是基于接口的儒喊,所以只能轉(zhuǎn)換為 Cat 實現(xiàn)的接口 Animal
Animal catProxy2 = proxy2.getProxy();
// 調(diào)用代理對象的方法
catProxy2.eat();
}
}
運行結(jié)果如下所示。
嵌套代理對象的結(jié)構(gòu)示意圖如下所示币呵。
這樣怀愧,我們就通過嵌套代理對象的方式實現(xiàn)了多重代理。
責(zé)任鏈模式一
首先余赢,我們定義一個 AbstractHandler
類芯义。
public abstract class AbstractHandler {
/**
* 責(zé)任鏈中下一個處理者
*/
@Setter
private AbstractHandler nextHandler;
/**
* 是否有下一個處理者
* @return
*/
public boolean hasNextHandler() {
return this.nextHandler != null;
}
abstract Object invoke(TargetMethod targetMethod) throws Throwable;
public final Object proceed(TargetMethod targetMethod) throws Throwable {
// 如果沒有下一個處理者,則直接調(diào)用目標(biāo)對象的被代理方法
if (!hasNextHandler()) {
return targetMethod.getMethod().invoke(targetMethod.getTarget(), targetMethod.getArgs());
}
// 否則調(diào)用下一個處理者的方法
return this.nextHandler.invoke(targetMethod);
}
/**
* 第一個 Handler妻柒,不做額外處理扛拨,起驅(qū)動責(zé)任鏈向前調(diào)用的作用
*/
public static class HeadHandler extends AbstractHandler {
@Override
Object invoke(TargetMethod targetMethod) throws Throwable {
return null;
}
}
}
AbstractHandler
類中有兩個核心方法,proceed
用來驅(qū)動責(zé)任鏈條向前執(zhí)行举塔,invoke
用來做目標(biāo)方法增強處理绑警。
然后再來修改一下 Spring AOP (二) JDK 動態(tài)代理 中提到的 JdkDynamicProxy
類。
@AllArgsConstructor
public class JdkDynamicProxy2 implements InvocationHandler {
/**
* 目標(biāo)對象(也被稱為被代理對象)
*/
private Object target;
/**
* 責(zé)任鏈 HeadHandler央渣,僅用來開始責(zé)任鏈執(zhí)行计盒,不對方法進行增強處理
*/
private AbstractHandler.HeadHandler headHandler;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
TargetMethod targetMethod = new TargetMethod();
targetMethod.setTarget(target);
targetMethod.setMethod(method);
targetMethod.setArgs(args);
return headHandler.proceed(targetMethod);
}
/**
* 獲取被代理接口實例對象
*
* 通過 Proxy.newProxyInstance 可以獲得一個代理對象,它實現(xiàn)了 target.getClass().getInterfaces() 接口
*
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
}
JdkDynamicProxy2
類中新增了 headHandler
字段芽丹,用來驅(qū)動責(zé)任鏈的執(zhí)行北启。
通過 Client2
類中的測試代碼,可以看到責(zé)任鏈模式是如何實現(xiàn)多重代理拔第。
public class Client2 {
public static void main(String[] args) {
// 1. 構(gòu)造目標(biāo)對象
Cat catTarget = new Cat();
// 2. 構(gòu)造 Handler 對象
AbstractHandler.HeadHandler headHandler = new AbstractHandler.HeadHandler();
AbstractHandler handler1 = new Handler1();
headHandler.setNextHandler(handler1);
Handler2 handler2 = new Handler2();
handler1.setNextHandler(handler2);
// 3. 根據(jù)目標(biāo)對象生成代理對象
JdkDynamicProxy2 proxy = new JdkDynamicProxy2(catTarget, headHandler);
// JDK 動態(tài)代理是基于接口的咕村,所以只能轉(zhuǎn)換為 Cat 實現(xiàn)的接口 Animal
Animal catProxy = proxy.getProxy();
// 調(diào)用代理對象的方法
catProxy.eat();
}
private static class Handler1 extends AbstractHandler {
@Override
Object invoke(TargetMethod targetMethod) throws Throwable {
System.out.println("Handler1 處理開始-------------------------------");
Object ret = super.proceed(targetMethod);
System.out.println("Handler1 處理完成-------------------------------");
return ret;
}
}
private static class Handler2 extends AbstractHandler {
@Override
Object invoke(TargetMethod targetMethod) throws Throwable {
System.out.println("Handler2 處理開始-------------------------------");
Object ret = super.proceed(targetMethod);
System.out.println("Handler2 處理完成-------------------------------");
return ret;
}
}
}
運行結(jié)果如下所示。
責(zé)任鏈模式一結(jié)構(gòu)示意圖如下所示楼肪。
這樣培廓,我們就通過責(zé)任鏈模式實現(xiàn)了多重代理。
責(zé)任鏈模式二
上面的責(zé)任鏈模式有些許不足之處春叫,比如需要一個 Head
對象驅(qū)動責(zé)任鏈模式的運行肩钠,鏈表結(jié)構(gòu)不易調(diào)整等等。
因此暂殖,我們通過數(shù)組的方式來改進責(zé)任鏈模式下的多重代理价匠。
首先,我們定義兩個接口呛每,MyMethodInvocation
和 MyMethodInterceptor
踩窖。
public interface MyMethodInvocation {
/**
* 進入攔截器鏈中的下一個攔截器,驅(qū)動責(zé)任鏈模式向前運行
*/
Object proceed() throws Throwable;
}
/**
* 在到達目標(biāo)方法之前攔截對方法的調(diào)用晨横。
*
* @Author: wilimm
* @Date: 2019/5/4 14:16
*/
public interface MyMethodInterceptor {
/**
* 類似于 InvocationHandler 的 invoke 方法洋腮,用于對方法做增強處理箫柳,并通過 invocation 參數(shù)驅(qū)動責(zé)任鏈向前運行
* @param invocation
* @return
* @throws Throwable
*/
Object invoke(MyMethodInvocation invocation) throws Throwable;
}
然后我們定義 MyMethodInvocationImpl
類,用來實現(xiàn) MyMethodInvocation
接口啥供。
@Data
public class MyMethodInvocationImpl implements MyMethodInvocation {
/**
* 攔截器鏈
*/
private List<MyMethodInterceptor> interceptorList;
/**
* 被代理的目標(biāo)方法
*/
private TargetMethod targetMethod;
/**
* 當(dāng)前調(diào)用的攔截器索引
*/
private int currentInterceptorIndex = 0;
@Override
public Object proceed() throws Throwable {
/**
* 索引值從 0 開始遞增悯恍,所以如果 currentInterceptorIndex 等于攔截器集合大小,說明所有的攔截器都執(zhí)行完畢了
*/
if (this.currentInterceptorIndex == this.interceptorList.size()) {
// 調(diào)用目標(biāo)方法
return targetMethod.getMethod().invoke(targetMethod.getTarget(), targetMethod.getArgs());
}
// 獲取下一個攔截器伙狐,并調(diào)用其 invoke 方法
MyMethodInterceptor methodInterceptor =
this.interceptorList.get(this.currentInterceptorIndex++);
return methodInterceptor.invoke(this);
}
}
前期準(zhǔn)備工作做完之后涮毫,我們可以來改造 Spring AOP (二) JDK 動態(tài)代理 中提到的 JdkDynamicProxy
類了。
@Data
public class JdkDynamicProxy3 implements InvocationHandler {
/**
* 目標(biāo)對象(也被稱為被代理對象)
*/
private Object target;
/**
* 攔截器鏈
*/
private List<MyMethodInterceptor> interceptorList = new ArrayList<>();
public void addMethodInterceptor(MyMethodInterceptor methodInterceptor) {
this.interceptorList.add(methodInterceptor);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
TargetMethod targetMethod = new TargetMethod();
targetMethod.setTarget(target);
targetMethod.setMethod(method);
targetMethod.setArgs(args);
MyMethodInvocationImpl methodInvocation = new MyMethodInvocationImpl();
methodInvocation.setTargetMethod(targetMethod);
methodInvocation.setInterceptorList(interceptorList);
return methodInvocation.proceed();
}
/**
* 獲取被代理接口實例對象
*
* 通過 Proxy.newProxyInstance 可以獲得一個代理對象贷屎,它實現(xiàn)了 target.getClass().getInterfaces() 接口
*
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
}
JdkDynamicProxy3
類中新增了 interceptorList
字段罢防,并用 MyMethodInvocationImpl
類來驅(qū)動責(zé)任鏈的執(zhí)行。
通過 Client3
類中的測試代碼唉侄,可以看到改進后的責(zé)任鏈模式比之前 Client2
中的責(zé)任鏈模式的使用優(yōu)雅了很多咒吐。
public class Client3 {
public static void main(String[] args) {
// 1. 構(gòu)造目標(biāo)對象
Cat catTarget = new Cat();
// 2. 根據(jù)目標(biāo)對象生成代理對象
JdkDynamicProxy3 proxy = new JdkDynamicProxy3();
proxy.setTarget(catTarget);
// 3. 添加方法攔截器
proxy.addMethodInterceptor(new MethodInterceptor1());
proxy.addMethodInterceptor(new MethodInterceptor2());
// JDK 動態(tài)代理是基于接口的,所以只能轉(zhuǎn)換為 Cat 實現(xiàn)的接口 Animal
Animal catProxy = proxy.getProxy();
// 調(diào)用代理對象的方法
catProxy.eat();
}
private static class MethodInterceptor1 implements MyMethodInterceptor {
@Override
public Object invoke(MyMethodInvocation invocation) throws Throwable {
System.out.println("MethodInterceptor1 處理開始-------------------------------");
Object ret = invocation.proceed();
System.out.println("MethodInterceptor1 處理完成-------------------------------");
return ret;
}
}
private static class MethodInterceptor2 implements MyMethodInterceptor {
@Override
public Object invoke(MyMethodInvocation invocation) throws Throwable {
System.out.println("MethodInterceptor2 處理開始-------------------------------");
Object ret = invocation.proceed();
System.out.println("MethodInterceptor2 處理完成-------------------------------");
return ret;
}
}
}
運行結(jié)果如下所示美旧。
責(zé)任鏈模式二結(jié)構(gòu)示意圖如下所示渤滞。
通過上面的內(nèi)容,我們已經(jīng)了解了如何通過嵌套代理對象和責(zé)任鏈模式實現(xiàn)多重代理榴嗅。
(正文完)