责任链模式 -- 多个拦截器

March . 13 . 2019
  • 责任链模式的应用场景

上一篇我们说到设计者一般会用拦截器来代替动态代理, 然后将拦截器的接口提供给开发者, 从而简化开发难度, 但是拦截器可能有多个。举个例子, 一个员工需要请假一周。如果把请假申请单看成一个对象, 那么它需要经过项目经理, 部门经理, 人事等多个角色的审批, 每个角色都有机会通过拦截这个申请单进行批阅和修改。此时的这三个部门就相当于三个拦截器, 而请假申请单则代表用户的请求。

1.JPG

当一个对象在一条链上被多个拦截器处理(拦截器来决定是否拦截)时, 这样的设计模式称为拦截器模式, 主要应用在一个对象在多个角色中传递的场景。

  • 责任链模式的实现方法

了解了责任链模式后, 我们来看看如何构建一个责任链。还是以刚才的栗子为例, 申请单走到项目经理, 项目经理可能把时间从'两周'改为'三周', 从而影响后面的审批, 后面的审批度需要根据前面的结果来进行。这个时候可以考虑使用层层代理来实现。当申请单走到项目经理处, 生成第一个动态代理proxy1, 当它走到部门经理处, 在proxy1的基础上生成proxy2, 同理当申请单走到人事后, 在proxy的基础上生成proxy3。实际情况中可能还会有更多的部门, 以此类推即可。

                                  2.JPG

  • 代码实现

项目包结构如下

3.JPG

目标对象类

Leave.java

package chain;

public class Leave implements Say {
    @Override
    public void sayHello(String name) {
        System.out.println("请假: " + name);
    }
}

由于是基于jdk的动态代理所以需要实现一个接口类。

Say.java

package chain;

public interface Say {
    void sayHello(String hello);
}

新建拦截器接口。

Interceptor.java

package chain;

import java.lang.reflect.Method;

public interface Interceptor {
    public boolean before(Object proxy, Object target, Method method, Object[] args);

    public void around(Object proxy, Object target, Method method, Object[] args);

    public void after(Object proxy, Object target, Method method, Object[] args);
}

新建拦截类, 实现具体的拦截逻辑。

Interceptor1.java

package chain;

import java.lang.reflect.Method;

public class Interceptor1 implements Interceptor {
    @Override
    public boolean before(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("[拦截器1] 的 before 方法");
        return true;
    }

    @Override
    public void around(Object proxy, Object target, Method method, Object[] args) {

    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("[拦截器1] 的 after 方法");
    }
}

Interceptor2.java

package chain;

import java.lang.reflect.Method;

public class Interceptor2 implements Interceptor {
    @Override
    public boolean before(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("[拦截器2] 的 before 方法");
        return true;
    }

    @Override
    public void around(Object proxy, Object target, Method method, Object[] args) {

    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("[拦截器2] 的 after 方法");
    }
}

Interceptor3.java

package chain;import java.lang.reflect.Method;public class Interceptor3 implements Interceptor {
    @Override    public boolean before(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("[拦截器3] 的 before 方法");        return true;    }

    @Override    public void around(Object proxy, Object target, Method method, Object[] args) {

    }

    @Override    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("[拦截器3] 的 after 方法");    }
}

新建代理类, 实现代理逻辑

InterceptorJdkProxy.java

 package chain;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class InterceptorJdkProxy implements InvocationHandler {

    private Object target;                    // 真实对象
    private String interceptorClass = null;   // 拦截器的全限定名

    public InterceptorJdkProxy(Object target, String interceptorClass)
    {
        this.target = target;
        this.interceptorClass = interceptorClass;
    }

    /**
     * 绑定委托对象并返回一个代理
     *
     * @param target 真实对象
     * @param interceptorClass 拦截器类全路径名
     * @return 代理对象
     */
    public static Object bind(Object target, String interceptorClass)
    {
        // 取得代理对象
        return  Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                       target.getClass().getInterfaces(),
                                       new InterceptorJdkProxy(target, interceptorClass));
    }

    /**
     * 通过代理对象进入方法, 首先进入这个方法
     *
     * @param proxy  代理对象
     * @param method 方法, 真实对象被调用的方法
     * @param args  方法的参数
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (interceptorClass == null) {
            return method.invoke(target, args);
        }

        Object result = null;

        // 通过反射生成拦截器
        Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance();

        // 调用前置方法
        if (interceptor.before(proxy, target, method, args)) {
            // 反射原有对象方法
            result = method.invoke(target, args);
        } else {  // 返回 false 执行 around 方法
            interceptor.around(proxy, target, method, args);
        }

        // 调用后置方法
        interceptor.after(proxy, target, method, args);
        return result;
    }
}

新建测试类

Test.java

package chain;

public class Test {
    public static void main(String[] args)
    {
        Say proxy1 = (Say) InterceptorJdkProxy.bind(new Leave(), "chain.Interceptor1");
        Say proxy2 = (Say) InterceptorJdkProxy.bind(proxy1, "chain.Interceptor2");
        Say proxy3 = (Say) InterceptorJdkProxy.bind(proxy2, "chain.Interceptor3");
        proxy3.sayHello("一周");
    }
}
  • 运行结果

4.JPG

before 方法会按照从最后一个拦截器到第一个拦截器的顺序运行, 而 after 方法则按照从第一个拦截器到最后一个拦截器的顺序运行。责任链模式的优点在于我们可以在传递链上加入新的逻辑, 程序的扩展性高, 但缺点是会增加代理和反射, 使程序性能下降。