Spring AOP (四) 多重代理和责任链模式
前面我们讲 JDK 动态代理和 CGLIB 动态代理时,都只说了一次代理,即对目标方法做一次增强操作。
下面我们来看看如何用 JDK 动态代理如何实现多重代理。
嵌套代理对象
本文中的代码用到 Lombok 注解,因此需要引入下面的 Maven 依赖。
<!--
Lombok能通过注解的方式,在编译时自动为属性生成构造器、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 动态代理 中提到的 JdkDynamicProxy
类。
@Data
public class JdkDynamicProxy1 implements InvocationHandler {
/**
* 目标对象(也被称为被代理对象)
*
* Java 代理模式的一个必要要素就是代理对象要能拿到被代理对象的引用
*/
private Object target;
/**
* 用来标识 InvocationHandler invoke 调用
*/
private String tag;
public JdkDynamicProxy1(Object target){
this.target=target;
}
/**
* 回调方法
* @param proxy JDK 生成的代理对象
* @param method 被代理的方法(也就是需要增强的方法)
* @param args 被代理方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 直接输出 hashCode 值,避免 invoke 递归调用,导致栈溢出
if (method.getName().equals("hashCode")) {
return hashCode();
}
System.out.println("JdkDynamicProxy1 invoke 方法执行前---------------" + info(proxy));
Object object= method.invoke(this.target, args);
System.out.println("JdkDynamicProxy1 invoke 方法执行后----------------" + info(proxy));
return object;
}
/**
* 输出代理类的一些信息,比如类名,hashCode 等
* @param proxy
* @return
*/
private String info(Object proxy) {
return proxy.getClass().getName() + ":" + proxy.hashCode() + "----------------" + this.tag;
}
/**
* 获取被代理接口实例对象
*
* 通过 Proxy.newProxyInstance 可以获得一个代理对象,它实现了 target.getClass().getInterfaces() 接口
*
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
}
JdkDynamicProxy1
类中新增了 tag
字段,用于标识 invoke
方法调用中的代理类,并在 invoke
方法调用中打印了代理类的类名和 hashCode
值。
Client1
类中的测试代码,通过多个代理对象嵌套的方式实现了多重代理。
public class Client1 {
public static void main(String[] args) {
// 1. 构造目标对象
Cat catTarget = new Cat();
// 2. 根据目标对象生成代理对象
JdkDynamicProxy1 proxy = new JdkDynamicProxy1(catTarget);
proxy.setTag("第一个代理类");
// JDK 动态代理是基于接口的,所以只能转换为 Cat 实现的接口 Animal
Animal catProxy = proxy.getProxy();
// 3. 根据第一个代理对象生成代理对象
JdkDynamicProxy1 proxy2 = new JdkDynamicProxy1(catProxy);
proxy2.setTag("第二个代理类");
// JDK 动态代理是基于接口的,所以只能转换为 Cat 实现的接口 Animal
Animal catProxy2 = proxy2.getProxy();
// 调用代理对象的方法
catProxy2.eat();
}
}
运行结果如下所示。

嵌套代理对象的结构示意图如下所示。

这样,我们就通过嵌套代理对象的方式实现了多重代理。
责任链模式一
首先,我们定义一个 AbstractHandler
类。
public abstract class AbstractHandler {
/**
* 责任链中下一个处理者
*/
@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 {
// 如果没有下一个处理者,则直接调用目标对象的被代理方法
if (!hasNextHandler()) {
return targetMethod.getMethod().invoke(targetMethod.getTarget(), targetMethod.getArgs());
}
// 否则调用下一个处理者的方法
return this.nextHandler.invoke(targetMethod);
}
/**
* 第一个 Handler,不做额外处理,起驱动责任链向前调用的作用
*/
public static class HeadHandler extends AbstractHandler {
@Override
Object invoke(TargetMethod targetMethod) throws Throwable {
return null;
}
}
}
AbstractHandler
类中有两个核心方法,proceed
用来驱动责任链条向前执行,invoke
用来做目标方法增强处理。
然后再来修改一下 Spring AOP (二) JDK 动态代理 中提到的 JdkDynamicProxy
类。
@AllArgsConstructor
public class JdkDynamicProxy2 implements InvocationHandler {
/**
* 目标对象(也被称为被代理对象)
*/
private Object target;
/**
* 责任链 HeadHandler,仅用来开始责任链执行,不对方法进行增强处理
*/
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 可以获得一个代理对象,它实现了 target.getClass().getInterfaces() 接口
*
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
}
JdkDynamicProxy2
类中新增了 headHandler
字段,用来驱动责任链的执行。
通过 Client2
类中的测试代码,可以看到责任链模式是如何实现多重代理。
public class Client2 {
public static void main(String[] args) {
// 1. 构造目标对象
Cat catTarget = new Cat();
// 2. 构造 Handler 对象
AbstractHandler.HeadHandler headHandler = new AbstractHandler.HeadHandler();
AbstractHandler handler1 = new Handler1();
headHandler.setNextHandler(handler1);
Handler2 handler2 = new Handler2();
handler1.setNextHandler(handler2);
// 3. 根据目标对象生成代理对象
JdkDynamicProxy2 proxy = new JdkDynamicProxy2(catTarget, headHandler);
// JDK 动态代理是基于接口的,所以只能转换为 Cat 实现的接口 Animal
Animal catProxy = proxy.getProxy();
// 调用代理对象的方法
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;
}
}
}
运行结果如下所示。

责任链模式一结构示意图如下所示。

这样,我们就通过责任链模式实现了多重代理。
责任链模式二
上面的责任链模式有些许不足之处,比如需要一个 Head
对象驱动责任链模式的运行,链表结构不易调整等等。
因此,我们通过数组的方式来改进责任链模式下的多重代理。
首先,我们定义两个接口,MyMethodInvocation
和 MyMethodInterceptor
。
public interface MyMethodInvocation {
/**
* 进入拦截器链中的下一个拦截器,驱动责任链模式向前运行
*/
Object proceed() throws Throwable;
}
/**
* 在到达目标方法之前拦截对方法的调用。
*
* @Author: wilimm
* @Date: 2019/5/4 14:16
*/
public interface MyMethodInterceptor {
/**
* 类似于 InvocationHandler 的 invoke 方法,用于对方法做增强处理,并通过 invocation 参数驱动责任链向前运行
* @param invocation
* @return
* @throws Throwable
*/
Object invoke(MyMethodInvocation invocation) throws Throwable;
}
然后我们定义 MyMethodInvocationImpl
类,用来实现 MyMethodInvocation
接口。
@Data
public class MyMethodInvocationImpl implements MyMethodInvocation {
/**
* 拦截器链
*/
private List<MyMethodInterceptor> interceptorList;
/**
* 被代理的目标方法
*/
private TargetMethod targetMethod;
/**
* 当前调用的拦截器索引
*/
private int currentInterceptorIndex = 0;
@Override
public Object proceed() throws Throwable {
/**
* 索引值从 0 开始递增,所以如果 currentInterceptorIndex 等于拦截器集合大小,说明所有的拦截器都执行完毕了
*/
if (this.currentInterceptorIndex == this.interceptorList.size()) {
// 调用目标方法
return targetMethod.getMethod().invoke(targetMethod.getTarget(), targetMethod.getArgs());
}
// 获取下一个拦截器,并调用其 invoke 方法
MyMethodInterceptor methodInterceptor =
this.interceptorList.get(this.currentInterceptorIndex++);
return methodInterceptor.invoke(this);
}
}
前期准备工作做完之后,我们可以来改造 Spring AOP (二) JDK 动态代理 中提到的 JdkDynamicProxy
类了。
@Data
public class JdkDynamicProxy3 implements InvocationHandler {
/**
* 目标对象(也被称为被代理对象)
*/
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 可以获得一个代理对象,它实现了 target.getClass().getInterfaces() 接口
*
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this);
}
}
JdkDynamicProxy3
类中新增了 interceptorList
字段,并用 MyMethodInvocationImpl
类来驱动责任链的执行。
通过 Client3
类中的测试代码,可以看到改进后的责任链模式比之前 Client2
中的责任链模式的使用优雅了很多。
public class Client3 {
public static void main(String[] args) {
// 1. 构造目标对象
Cat catTarget = new Cat();
// 2. 根据目标对象生成代理对象
JdkDynamicProxy3 proxy = new JdkDynamicProxy3();
proxy.setTarget(catTarget);
// 3. 添加方法拦截器
proxy.addMethodInterceptor(new MethodInterceptor1());
proxy.addMethodInterceptor(new MethodInterceptor2());
// JDK 动态代理是基于接口的,所以只能转换为 Cat 实现的接口 Animal
Animal catProxy = proxy.getProxy();
// 调用代理对象的方法
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;
}
}
}
运行结果如下所示。

责任链模式二结构示意图如下所示。

通过上面的内容,我们已经了解了如何通过嵌套代理对象和责任链模式实现多重代理。
(正文完)
扩展阅读
喜欢本文的朋友们,欢迎关注微信公众号【程序员小课堂】,阅读更多精彩内容!
