SpringFramework需要深入研究

Spring AOP (四) 多重代理和责任链模式

2019-05-06  本文已影响95人  蓝笔头

前面我们讲 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();
    }
}

运行结果如下所示。

输出结果.jpg

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

嵌套代理对象的结构示意图.jpg

这样,我们就通过嵌套代理对象的方式实现了多重代理。

责任链模式一

首先,我们定义一个 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;
        }
    }
}

运行结果如下所示。

输出结果.jpg

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

责任链模式一结构示意图.png

这样,我们就通过责任链模式实现了多重代理。

责任链模式二

上面的责任链模式有些许不足之处,比如需要一个 Head 对象驱动责任链模式的运行,链表结构不易调整等等。

因此,我们通过数组的方式来改进责任链模式下的多重代理。

首先,我们定义两个接口,MyMethodInvocationMyMethodInterceptor

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;
        }
    }
}

运行结果如下所示。

输出结果.jpg

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

责任链模式二结构示意图.png

通过上面的内容,我们已经了解了如何通过嵌套代理对象责任链模式实现多重代理。

(正文完)

本文所用代码地址

扩展阅读

喜欢本文的朋友们,欢迎关注微信公众号【程序员小课堂】,阅读更多精彩内容!


程序员小课堂.jpg
上一篇 下一篇

猜你喜欢

热点阅读