zoukankan      html  css  js  c++  java
  • 代理模式增强之路(代理+责任链模式)

            关于各种代理的基本信息,请查看代理模式详解。本文主要讲解基于JDK的动态代理的组件化封装之路。

            首先,我们看一下传统的使用方式:

    1,接口

    package effectiveJava.proxy;
    
    public interface HelloService {
        void sayHello();
    }

    2,代理元

    package effectiveJava.proxy;
    
    public class HelloServiceImpl implements HelloService{
        @Override
        public void sayHello() {
            System.out.println("Hello Proxy.");
        }
    }

    3,代理类(必须实现InvocationHandler接口)

    package effectiveJava.proxy.v0;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class ProxyInvocation implements InvocationHandler {
        /**
         * 代理元
         */
        private Object target;
    
        public ProxyInvocation(Object target) {
            this.target = target;
        }
    
        /**
         *
         * @param proxy 代理类实例
         * @param method 实际要调用的方法
         * @param args  实际要调用方法的参数类型
         * @return 结果值
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("HelloInvocation : Before Hello....");
            Object reslut = method.invoke(target, args);
            System.out.println("HelloInvocation : After Hello....");
            return reslut;
        }
    
    }

    4,测试类

    package effectiveJava.proxy.v0;
    
    import effectiveJava.proxy.HelloService;
    import effectiveJava.proxy.HelloServiceImpl;
    
    import java.lang.reflect.Proxy;
    
    /**
    * 通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)创建代理对象的实例
    */
    public class HelloInvocationDemo {
        public static void main(String[] args) {
            HelloServiceImpl helloService = new HelloServiceImpl();
            ProxyInvocation helloInvocation = new ProxyInvocation(helloService);
            HelloService impl = (HelloService)Proxy.newProxyInstance(
                    helloService.getClass().getClassLoader(),
                    helloService.getClass().getInterfaces(),
                    helloInvocation);
            impl.sayHello();
        }
    }

               我们发现,客户端每次使用代理的时候,都必须通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)创建代理对象的实例,使用不太方便。我们可以将该方法封装到代理类(ProxyInvocation)中,简化客户端的调用。代码修改如下:

    3,代理类(必须实现InvocationHandler接口)
    package effectiveJava.proxy.v1;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyInvocation implements InvocationHandler {
        /**
         * 代理元
         */
        private Object target;
    
        public ProxyInvocation(Object target) {
            this.target = target;
        }
    
        /**
         *
         * @param proxy 代理类实例
         * @param method 实际要调用的方法
         * @param args  实际要调用方法的参数类型
         * @return 结果值
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before Hello....");
            Object reslut = method.invoke(target, args);
            System.out.println("after Hello....");
            return reslut;
        }
    
        public static Object getProxyObject(Object target) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new ProxyInvocation(target));
        }
    }

    4,测试类

    package effectiveJava.proxy.v1;
    
    import effectiveJava.proxy.HelloService;
    import effectiveJava.proxy.HelloServiceImpl;
    
    public class HelloInvocationDemo {
        public static void main(String[] args) {
            HelloService impl = (HelloService)ProxyInvocation.getProxyObject(new HelloServiceImpl());
            impl.sayHello();
        }
    }

              出于组建化考虑,我们可以把代理类封装成一个组件,因此我们需要将可能产生变化的逻辑剥离出来(3中黄色部分)。我们可以定义一个拦截器接口,用来封装这些动态逻辑,然后用代理类(ProxyInvocation)调用这个拦截器的方法。而动态逻辑在拦截器接口的具体实现类中实现。修改后代码代码如下:

    3,拦截器接口

    package effectiveJava.proxy.v2.jar;
    
    public interface Interceptor {
        void intercept();
    }

    4,代理类

    package effectiveJava.proxy.v2.jar;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.List;
    
    public class ProxyInvocation implements InvocationHandler {
    
        private Object target;
        private List<Interceptor> beforeInterceptors;
        private List<Interceptor> afterInterceptors;
    
        public ProxyInvocation(Object target, List<Interceptor> beforeInterceptors, List<Interceptor> afterInterceptors) {
            this.target = target;
            this.beforeInterceptors = beforeInterceptors;
            this.afterInterceptors = afterInterceptors;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            for(Interceptor interceptor : beforeInterceptors) {
                interceptor.intercept();
            }
    
            Object reslut = method.invoke(target, args);
    
            for(Interceptor interceptor : afterInterceptors) {
                interceptor.intercept();
            }
    
            return reslut;
        }
    
        public static Object getProxyObject(Object target,List<Interceptor> beforeInterceptors,List<Interceptor> afterInterceptors) {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new ProxyInvocation(target,beforeInterceptors,afterInterceptors));
        }
    }

    5,拦截器的实现类

    package effectiveJava.proxy.v2;
    
    import effectiveJava.proxy.v2.jar.Interceptor;
    
    public class BeforeInterceptor implements Interceptor {
        @Override
        public void intercept() {
            System.out.println("BeforeInterceptor : Before Hello....");
        }
    }
    package effectiveJava.proxy.v2;
    
    import effectiveJava.proxy.v2.jar.Interceptor;
    
    public class AfterIntercept implements Interceptor {
        @Override
        public void intercept() {
            System.out.println("AfterIntercept : After Hello....");
        }
    }

    6,测试类

    package effectiveJava.proxy.v2;
    
    import effectiveJava.proxy.HelloService;
    import effectiveJava.proxy.HelloServiceImpl;
    import effectiveJava.proxy.v2.jar.ProxyInvocation;
    
    import java.util.Arrays;
    
    public class HelloInvocationDemo {
        public static void main(String[] args) {
            HelloService impl = (HelloService) ProxyInvocation.getProxyObject(
                    new HelloServiceImpl(),
                    Arrays.asList(new BeforeInterceptor()),
                    Arrays.asList(new AfterIntercept()));
            impl.sayHello();
        }
    }

            上述方法中,代理类中的invoke()需要分别便利前置通知和后置通知,比较繁琐,需要再次优化。可以通过创建Invocation类把拦截对象信息进行封装,作为拦截器拦截方法的参数,把拦截目标对象真正的执行方法放到Interceptor中完成。修改后代码代码如下:

    3,拦截对象

    package effectiveJava.proxy.v3.jar;
    
    import java.lang.reflect.Method;
    
    /**
    * 封装拦截的目标,包含:类实例、方法、参数
    * @author Winn
    */
    public class Invocation {
    
        private Object target;
        private Method method;
        private Object[] args;
    
        public Invocation(Object target, Method method, Object[] args) {
            this.target = target;
            this.method = method;
            this.args = args;
        }
    
        public Object invoke() throws Throwable {
            return method.invoke(target, args);
        }
    }

    4, 拦截器接口【代理对象的实例在还接口的方法中创建】

    package effectiveJava.proxy.v3.jar;
    
    public interface Interceptor {
    
        Object intercept(Invocation invocation) throws Throwable;
    
    /**
    *使用JDK8中的接口默认方法
    */
    default Object wrap(Object target) { return ProxyInvocationHandler.getProxyObject(target,this); } }

    5,代理类

    package effectiveJava.proxy.v3.jar;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyInvocationHandler implements InvocationHandler {
    
        private Object target;
        private Interceptor interceptor;
    
        public ProxyInvocationHandler(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 getProxyObject(Object target, Interceptor interceptor) {
            ProxyInvocationHandler handler = new ProxyInvocationHandler(target, interceptor);
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
        }
    }

    6,拦截器的实现类(添加附加逻辑,并调用代理元)

    package effectiveJava.proxy.v3;
    
    import effectiveJava.proxy.v3.jar.Interceptor;
    import effectiveJava.proxy.v3.jar.Invocation;
    
    public class LogIntercept implements Interceptor {
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("LogIntercept : After Hello....");
            Object invoke = invocation.invoke();
            System.out.println("LogIntercept : After Hello....");
            return invoke;
        }
    }

    7,测试类

    package effectiveJava.proxy.v3;
    
    import effectiveJava.proxy.HelloService;
    import effectiveJava.proxy.HelloServiceImpl;
    
    public class HelloInvocationDemo {
        public static void main(String[] args) {
            HelloService target = new HelloServiceImpl();
            //拦截器调用两次,日志输出两遍
            target = (HelloService)new LogIntercept().wrap(target);
            target = (HelloService)new LogIntercept().wrap(target);
            target.sayHello();
        }
    }

            至此,将代理进行组件化封装已经告一段落。拦截对象(3),拦截器接口(4),代理类(5)可以封装成一个jar包。然后根据业务,自定义需要的接口(1)、接口实现类(2),以及拦截器(6)。由于我们可能需要多个不同功能的拦截器,通过上述方式,我们需要不停地在客户端调用各个拦截器实例的wrap()方法,这样不太美观。我们利用责任链模式的思想,再将拦截器封装到一个集合里,用这个集合容器去实现调用。修改后代码代码如下:

    7,拦截器链

    package effectiveJava.proxy.v4.jar;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class InterceptChain {
    
        private List<Interceptor> interceptorList = new ArrayList<>();
    
        public void regist(Interceptor interceptor) {
            interceptorList.add(interceptor);
        }
    
        public void registAll(Interceptor ... interceptors) {
            for(Interceptor interceptor : interceptors) {
                regist(interceptor);
            }
        }
    
        public Object wrap(Object target) {
            for(Interceptor interceptor : interceptorList) {
                target = interceptor.wrap(target);
            }
            return target;
        }
    }

    8,测试类

    package effectiveJava.proxy.v4;
    
    import effectiveJava.proxy.HelloService;
    import effectiveJava.proxy.HelloServiceImpl;
    import effectiveJava.proxy.v4.jar.InterceptChain;
    
    public class HelloInvocationDemo {
        public static void main(String[] args) {
            InterceptChain chain = new InterceptChain();
            chain.registAll(new LogIntercept(),new LogIntercept(),new LogIntercept());
            HelloService target = new HelloServiceImpl();
            target = (HelloService)chain.wrap(target);
            target.sayHello();
        }
    }
    根据不断地优化封装,产生了多个版本的代码,各个版本的完成代码请查看https://github.com/winn-hu/summary/tree/master/comm/src/main/java/effectiveJava/proxy
  • 相关阅读:
    UltraWebGrid多表头
    2009个人年度总结报告(IT)
    DevExpress分发
    AspxTreeList数据绑定以及模板和外观定制的运用
    每日一句英语:No problem, Mr. Smith. Anything else?
    “向程序发送命令时出现问题”的解决方法
    ASP常用进制转化类(2,8,10,16,32,64)
    我的分页用户控件(性能问题)
    研发的那些事2—设计之惑
    一个架构的演化2用ESB集成
  • 原文地址:https://www.cnblogs.com/BlueStarWei/p/14023034.html
Copyright © 2011-2022 走看看