从头开始了解学习拦截器、认识责任链模式

由于动态代理之前介绍过的(JDK动态代理CGLIB动态代理)在实际开发一般比较难理解,程序设计者会设计一个拦截器接口供考法这使用,开发者只需知道拦截器接口的方法,含义和作用即可,无需 知道动态代理是怎么实现的,用JDK动态代理来实现一个拦截器的逻辑,为此先定义拦截器接口,代码如下:

定义拦截器接口
/**
 * @author AmVilCres 
 * <p>
 *  该接口定义了3个方法:before、around、after
 *  <li>3个方法的参数为:proxy代理对象、target真实的对象、method方法、args运行方法参数</li>
 *  <li>before方法返回boolean值,它在真实对象前调用。当返回true时,则反射真实对象的方法;当返回false时,则调用around方法</li>
 *  <li>在反射真实对象方法或者around方法执行之后,调用after方法</li>
 * </p>
 */
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);
}
接口的上面注释已经写得很明白了,相信大家都能看懂,接下来写一个接口的实现类:
public class MyInterceptor implements Interceptor {

    @Override
    public boolean before(Object proxy, Object target, Method method, Object[] args) {
        System.err.println("反射方法前逻辑");
        return false;  //不反射被代理对象的原有方法
    }

    @Override
    public void around(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("取代了被代理对象的方法");
    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("反射方法后逻辑");
    }
}

使用JDK动态代理,就可以去实现这些方法在适当时调用的逻辑了,JDK动态代理中使用拦截器代码如下:

public class InterceptorJdkProxy implements InvocationHandler{
    private Object target; //真实对象
    private String interceptorClass; // 拦截器全限定名

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

    /**
     * 绑定委托对象并返回一个 【代理占位】
     * 
     * @param target 真实的对象
     * @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 方法的参数
     * */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(interceptorClass == null)
            return method.invoke(target, args);
        Object res = null;

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

        if(interceptor.before(proxy, target, method, args))
            res = method.invoke(target, args); //反射原有对象方法
        else {
            interceptor.around(proxy, target, method, args);
        }
        interceptor.after(proxy, target, method, args);

        return res;
    }
}

    代码说明:这里有两个属性,一个是target,它是真实的对象;另一个是字符串interceptorClass,它是一个类的全限定名
     执行步骤:
      1. 在bind方法中用JDK动态代理绑定一个对象,然后返回代理对象
      2. 如果没有设置拦截器,则直接反射真实对象的方法,然后结束,否则进行第3步,
      3. 通过反射生成拦截器,并准备使用它
      4. 调用拦截器的before方法,如果返回true,反射原来的方法,否则运行拦截器的around方法
      5. 调用拦截的after方法
      6. 返回结果

拦截器工作流程图:
拦截器工作流程图
测试代码:

public class TestInterceptorJdkProxy {
    public static void main(String[] args) {
        // "com.avc.interceptor.MyInterceptor"我定义的为拦截器的全限定名
        HelloWorld proxy = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(), "com.avc.interceptor.MyInterceptor");
        proxy.sayHello();
    }
}

运行结果:
这里写图片描述

责任链模式

上面讨论了设计者可能会用拦截器去代替动态代理,然后将接口提供给开发者,从而简化开发者难度,但是拦截器可能有多个。举个栗子, 某人需要请假一周,如果把请假申请单看成一个对象,那么他需要经过项目经理、部门经理、人事等多个角色的审批,每个角色都有机会通过拦截这个申请进行审批或者修改。这个时候就要考虑提供这3个角色的处理逻辑,所以需要提供3个拦截器,而传递的则是请假申请单 如图:
请假示例
当一个对象在一条链被多个拦截器拦截处理(拦截器也可以选择不拦截他)时,就把这样的设计模式称为责任链模式,它用于一个对象在多个角色中传递的场景。还是刚才的例子,申请单走到项目经理那,可能吧时间改为5天,从而影响了后面的审批,后面的审批都要根据前面的结果进行。这个时候考虑用层层代理来实现,就是当申请单(target)走到项目经理处,使用第一个动态代理proxy1,走到部门经理处,部门经理会得到一个在项目经理的代理proxy1基础上生成的proxy2,当走到人事的时候,会在proxy2的及出生成proxy3,如果还有其他角色,以此类推,下图描述会更清晰:
这里写图片描述
定义3个拦截器(实现上面定义的接口)

拦截器1public 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) {
        System.out.println("拦截器  1 的 around方法");
    }
    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("拦截器  1 的 after方法");
    }
}
----------------------------------
拦截器2:
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) {
        System.out.println("拦截器  2 的 around方法");
    }
    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("拦截器  2 的 after方法");
    }
}
---------------------------------------
拦截器3public 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) {
        System.out.println("拦截器  3 的 around方法");
    }
    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("拦截器  3 的 after方法");
    }
}

测试类:
public class LinkInterceptorTest {

    public static void main(String[] args) {
        HelloWorld proxy1 = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(), "com.avc.interceptor.link.Interceptor1");
        HelloWorld proxy2 = (HelloWorld) InterceptorJdkProxy.bind(proxy1, "com.avc.interceptor.link.Interceptor2");
        HelloWorld proxy3 = (HelloWorld) InterceptorJdkProxy.bind(proxy2, "com.avc.interceptor.link.Interceptor3");
        proxy3.sayHello();
    }
}

运行结果:
结果
可以通过改变before方法的返回值,从而得到不同的结果!
代码下载:上述代码的源码,导入eclipse即可运行,有问题请留言

猜你喜欢

转载自blog.csdn.net/sjzao/article/details/79472366
今日推荐