JDK动态代理+责任链设计模式

JDK动态代理+责任链设计模式

Mybatis 的插件机制,运用的就是该设计模式 ;

文章转自大牛: 雨点的名字

  • JDK动态代理案例
public class MyProxy {
    /**
     * 一个接口
     */
    public interface HelloService{
        void sayHello();
    }
    /**
     * 目标类实现接口
     */
    static class HelloServiceImpl implements HelloService{

        @Override
        public void sayHello() {
            System.out.println("sayHello......");
        }
    }
    /**
     * 自定义代理类需要实现InvocationHandler接口
     */
    static  class HWInvocationHandler implements InvocationHandler {
        /**
         * 目标对象
         */
        private Object target;

        public HWInvocationHandler(Object target){
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("------插入前置通知代码-------------");
            //执行相应的目标方法
            Object rs = method.invoke(target,args);
            System.out.println("------插入后置处理代码-------------");
            return rs;
        }

        public static Object wrap(Object target) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),new HWInvocationHandler(target));
        }
    }
    public static void main(String[] args)  {
        HelloService proxyService = (HelloService) HWInvocationHandler.wrap(new HelloServiceImpl());
        proxyService.sayHello();
    }
}

运行结果

------插入前置通知代码-------------
sayHello......
------插入后置处理代码-------------
  • 优化

    上面代理的功能是实现了,但是有个很明显的缺陷,就是 HWInvocationHandler 是动态代理类,也可以理解成是个工具类,我们不可能会把业务代码写到写到到invoke方法里,
    不符合面向对象的思想,可以抽象一下处理。可以设计一个Interceptor接口,需要做什么拦截处理实现接口就行了。

public interface Interceptor {
    /**
     * 具体拦截处理
     */
    void intercept();
}

intercept() 方法就可以处理各种前期准备了

public class LogInterceptor implements Interceptor {
    @Override
    public void intercept() {
        System.out.println("------插入前置通知代码-------------");
    }
}

public class TransactionInterceptor implements Interceptor {
    @Override
    public void intercept() {
        System.out.println("------插入后置处理代码-------------");
    }
}
  • 代理对象也做一下修改
public class HWInvocationHandler implements InvocationHandler {

    private Object target;

    private List<Interceptor> interceptorList = new ArrayList<>();

    public TargetProxy(Object target,List<Interceptor> interceptorList) {
        this.target = target;
        this.interceptorList = interceptorList;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       //处理多个拦截器
        for (Interceptor interceptor : interceptorList) {
            interceptor.intercept();
        }
        return method.invoke(target, args);
    }

    public static Object wrap(Object target,List<Interceptor> interceptorList) {
        HWInvocationHandler targetProxy = new HWInvocationHandler(target, interceptorList);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                                      target.getClass().getInterfaces(),targetProxy);
    }
}

现在可以根据需要动态的添加拦截器了,在每次执行业务代码sayHello()之前都会拦截一下,看起来高级一点,来测试一下

public class Test {
    public static void main(String[] args) {
        List<Interceptor> interceptorList = new ArrayList<>();
        interceptorList.add(new LogInterceptor());
        interceptorList.add(new TransactionInterceptor());

        HelloService target = new HelloServiceImpl();
        Target targetProxy = (Target) TargetProxy.wrap(target,interceptorList);
        targetProxy.sayHello();
    }
}

运行结果

------插入前置通知代码-------------
------插入后置处理代码-------------
sayHello......
  • 再优化
    上面的动态代理确实可以把代理类中的业务逻辑抽离出来,但是我们注意到,
    只有前置代理,无法做到前后代理,那么我们还需要再优化。
    思路:
    ① 我们可以做进一步的抽象,把拦截对象信息进行封装一下,
    作为拦截器拦截方法的参数

    ② 把拦截目标对象真正的执行方法放到 Interceptor中完成,
    这样就可以实现前后拦截,并且还能对拦截对象的参数等做修改。

设计一个Invocation 对象。

public class Invocation {

    /**
     * 目标对象
     */
    private Object target;
    /**
     * 执行的方法
     */
    private Method method;
    /**
     * 方法的参数
     */
    private Object[] args;
    
    //省略getset
    public Invocation(Object target, Method method, Object[] args) {
        this.target = target;
        this.method = method;
        this.args = args;
    }

    /**
     * 执行目标对象的方法
     */
    public Object process() throws Exception{
       return method.invoke(target,args);
    }
}

Interceptor拦截接口做修改

public interface Interceptor {
    /**
     * 具体拦截处理
     */
    Object intercept(Invocation invocation) throws Exception;
}

Interceptor实现类

public class TransactionInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Exception{
        System.out.println("------插入前置通知代码-------------");
        Object result = invocation.process();
        System.out.println("------插入后置处理代码-------------");
        return result;
    }
}

Invocation 类就是被代理对象的封装,也就是要拦截的真正对象。HWInvocationHandler修改如下:

public class HWInvocationHandler implements InvocationHandler {

    private Object target;

    private Interceptor interceptor;

    public TargetProxy(Object target,Interceptor interceptor) {
        this.target = target;
        this.interceptor = interceptor;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Invocation invocation = new Invocation(target,method,args);
        return interceptor.intercept(invocation);
    }

    public static Object wrap(Object target,Interceptor interceptor) {
        HWInvocationHandler targetProxy = new HWInvocationHandler(target, interceptor);
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),targetProxy);
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        HelloService target = new HelloServiceImpl();
        Interceptor transactionInterceptor = new TransactionInterceptor();
        HelloService targetProxy = (Target) TargetProxy.wrap(target,transactionInterceptor);
        targetProxy.sayHello();
    }
}

运行结果

------插入前置通知代码-------------
sayHello......
------插入后置处理代码-------------
  • 再再优化

    上面这样就能实现前后拦截,并且拦截器能获取拦截对象信息。
    但是测试代码的这样调用看着别扭,对应目标类来说,只需要了解对他
    插入了什么拦截就好。

再修改一下,在拦截器增加一个插入目标类的方法。

public interface Interceptor {
    /**
     * 具体拦截处理
     */
    Object intercept(Invocation invocation) throws Exception;

    /**
     *  插入目标类
     */
    Object plugin(Object target);

}

public class TransactionInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Exception{
        System.out.println("------插入前置通知代码-------------");
        Object result = invocation.process();
        System.out.println("------插入后置处理代码-------------");
        return result;
    }

    @Override
    public Object plugin(Object target) {
        return TargetProxy.wrap(target,this);
    }
}

这样目标类仅仅需要在执行前,插入需要的拦截器就好了,测试代码:

public class Test {
    public static void main(String[] args) {
        HelloService target = new HelloServiceImpl();
        Interceptor transactionInterceptor = new TransactionInterceptor();
        //把事务拦截器插入到目标类中
        target = (HelloService) transactionInterceptor.plugin(target);
        target.sayHello();
    }
}

运行结果

------插入前置通知代码-------------
sayHello......
------插入后置处理代码-------------
  • 多个拦截器如何处理
    到这里就差不多完成了,那我们再来思考如果要添加多个拦截器呢,怎么搞?
public class Test {
    public static void main(String[] args) {
        HelloService target = new HelloServiceImpl();
        Interceptor transactionInterceptor = new TransactionInterceptor();
        target = (HelloService) transactionInterceptor.plugin(target);
        LogInterceptor logInterceptor = new LogInterceptor();
        target = (HelloService)logInterceptor.plugin(target);
        target.sayHello();
    }
}

运行结果

------插入前置通知代码-------------
------插入前置通知代码-------------
sayHello......
------插入后置处理代码-------------
------插入后置处理代码------------
  • 责任链设计模式
    其实上面已经实现的没问题了,只是还差那么一点点,添加多个拦截器的时候不太美观,让我们再次利用面向对象思想封装一下。我们设计一个InterceptorChain 拦截器链类
public class InterceptorChain {

    private List<Interceptor> interceptorList = new ArrayList<>();

    /** 
     * 插入所有拦截器
     */
    public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptorList) {
            target = interceptor.plugin(target);
        }
        return target;
    }

    public void addInterceptor(Interceptor interceptor) {
        interceptorList.add(interceptor);
    }
    /**
     * 返回一个不可修改集合,只能通过addInterceptor方法添加
     * 这样控制权就在自己手里
     */
    public List<Interceptor> getInterceptorList() {
        return Collections.unmodifiableList(interceptorList);
    }
}

其实就是通过pluginAll() 方法包一层把所有的拦截器插入到目标类去而已。测试代码:

public class Test {

    public static void main(String[] args) {
        HelloService target = new HelloServiceImpl();
        Interceptor transactionInterceptor = new TransactionInterceptor();
        LogInterceptor logInterceptor = new LogInterceptor();
        InterceptorChain interceptorChain = new InterceptorChain();
        interceptorChain.addInterceptor(transactionInterceptor);
        interceptorChain.addInterceptor(logInterceptor);
        target = (Target) interceptorChain.pluginAll(target);
        target.sayHello();
    }
}

这里展示的是JDK动态代理+责任链设计模式,那么Mybatis拦截器就是基于该组合进行开发。

你可能感兴趣的:(设计模式)