基于JDK和CGLib的链式动态代理

动态代理与链式动态代理

  链式代理就是将多个代理串链在一起,一个一个的去执行,执行的顺序取决于链上的先后顺序。JDK的动态代理只能代理实现了接口的类实例,而CGLib则可以代理普通的类实例。

执行链式代理的样子大概如下图所示:这很像Spring Aop的前后增强,实际上我也是基于这个想到,如果增强有多个,那么就是链式动态代理的样子了。好像Filter也是这样的,Node.js中如Express的中间件也是这样的。

        /*
        * a.before()
        *       b.before()
        *       执行被代理的方法
        *       b.after()
        *a.after()
        *
         */

类图及测试结果


类图


TestMain是执行类。将被代理class和Proxy的链表list以参数传入到ProxyManager中,生成被代理的实例,执行被代理的实例的属性方法。


测试结果

dynamic.proxy.ProxyImplFirst@25c78000        ImplFirst before
dynamic.proxy.ProxyImplSecond@4ca49360                        Impl Second  before
TestMain$$EnhancerByCGLIB$$59702c51 ------------Hello-------- 
dynamic.proxy.ProxyImplSecond@4ca49360                        Impl Second  after
dynamic.proxy.ProxyImplFirst@25c78000        ImplFirst after

dynamic.proxy.ProxyImplFirst@25c78000        ImplFirst before
dynamic.proxy.ProxyImplSecond@4ca49360                        Impl Second  before
TestMain ------------Hello-------- 
dynamic.proxy.ProxyImplSecond@4ca49360                        Impl Second  after
dynamic.proxy.ProxyImplFirst@25c78000        ImplFirst after

第一个组输出为CGLIB的动态代理结果,第二组输出为JDK的动态代理结果。


主要代码


ProxyManager


CgProxyManager

    /**
         * 根据cls,和代理链生成Proxy代理对象
         * cls不必含有接口,CGLib本身决定的。
         *
         * @param targetClass
         * @param proxyList
         * @return
         */
        public   T createProxy(final Class targetClass, final List proxyList) {
            return (T) Enhancer.create(targetClass, new MethodInterceptor() {
                @Override
                public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
                    return new CgProxyChain(proxyList, targetClass, targetObject, targetMethod, methodProxy, methodParams).doProxyChain();
                }
            });
        }

JdkProxyManager

/**
         * 根据cls,和代理链生成Proxy代理对象
         * cls必须含有接口,JDK要求的。
         *
         * @param cls
         * @param proxyList
         * @return
         */
        @SuppressWarnings("unchecked")
        public   T createProxy(final Class cls, final List proxyList) {
            return (T) java.lang.reflect.Proxy.newProxyInstance(cls.getClassLoader(),
                    cls.getInterfaces(), new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            return new JdkProxyChain(proxyList, cls, cls.newInstance(), method, args).doProxyChain();
                        }
                    });
        }

可以看出在对被代理方法进行拦截以后都执行的是一个叫doProxyChain()的做链式代理的方法,可以猜想,这个方法肯定是做链式处理的,先处理第一个Proxy然后继续直到没有Proxy就执行被代理方法,然后返回被代理方法的返回值。那么是如何做到依次执行被代理方法的呢。


抽象类ProxyChain

public abstract class ProxyChain {
        List proxyList; //代理链
        Class targetClass; //被代理类
        Object targetObject; //被代理Object
        Method targetMethod; //被代理方法
        MethodProxy methodProxy; //被代理方法的Proxy,用于CGLib
        Object[] methodParams; //方法参数
       public int proxyIndex=0; //指向代理链的当前执行位置

        /*用于CGLib*/
        public ProxyChain(List proxyList, Class targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams) {
            this.proxyList = proxyList;
            this.targetClass = targetClass;
            this.targetObject = targetObject;
            this.targetMethod = targetMethod;
            this.methodProxy = methodProxy;
            this.methodParams = methodParams;
        }

        /*用于JDk*/
        public ProxyChain(List proxyList, Class targetClass, Object targetObject, Method targetMethod, Object[] methodParams) {
            this.proxyList = proxyList;
            this.targetClass = targetClass;
            this.targetObject = targetObject;
            this.targetMethod = targetMethod;
            this.methodParams = methodParams;
        }

        ...

        /*执行代理机制不一样,子类实现。*/
        public abstract Object doProxyChain() throws Throwable;

    }

CgProxyChain

    @Override
    public Object doProxyChain() throws Throwable {
            Object methodResult;
            /*没有执行链式的最后 则依次执行链式代理*/
            /*使用proxyIndex充当计数器,如果没到最后,则取出响应的Proxy对象*/
            /*并调用doProxy方法,在Proxy接口的实现中提供相应的横切逻辑,并再次代用doProxyChain直到次数达到*/
            /*最后,调用methodProxy的invokeSuper(),执行目标对象的业务逻辑*/
            if (proxyIndex < proxyList.size()) {
                methodResult = proxyList.get(proxyIndex++).doProxy(this);
            } else {
                methodResult = methodProxy.invokeSuper(targetObject, methodParams);
            }
            return methodResult;
        }

JdkProxyChain

 @Override
    public Object doProxyChain() throws Exception {
        Object methodResult;
        /*没有执行链式的最后 则依次执行链式代理*/
        /*使用proxyIndex充当计数器,如果没到最后,则取出响应的Proxy对象*/
        /*并调用doProxy方法,在Proxy接口的实现中提供相应的横切逻辑,并再次代用doProxyChain直到次数达到*/
        /*最后,调用methodProxy的invoke,执行目标对象的业务逻辑*/
        if (proxyIndex < proxyList.size()) {
            methodResult = proxyList.get(proxyIndex++).doProxy(this);
        } else {
            methodResult = targetMethod.invoke(targetObject, methodParams);
        }
        return methodResult;
    }

由上述可以看出一个执行的是invokeSuper一个执行的invoke方法,实际上大体是一样的,只是在学习动态代理的时候就是如此,那就如此吧。果然,我刚才试了一下,有问题!!所以这个方法针对不同的动态代理得重写。


几个代理类


代理类是这样的:定义一个接口,实现类1实现接口,实现类2继承实现类1,并复写里面的方法,达到2个代理实现类的目的。

Proxy

public interface Proxy {
        /**
         * 执行链式代理
         * @param proxyChain
         * @throws Exception
         */
        Object doProxy(ProxyChain proxyChain) throws Exception;
    }

ProxyImplFirst

public class ProxyImplFirst implements Proxy {
        /**
         * 执行链式代理
         *
         * @param proxyChain
         * @throws Exception
         */
        @Override
        public Object doProxy(ProxyChain proxyChain) throws Exception {
            Object res = null;
            Class cls = proxyChain.getTargetClass();
            Method method = proxyChain.getTargetMethod();
            Object[] params = proxyChain.getMethodParams();
            try {
                if (intercept(cls, method, params)) {
                    before(proxyChain);
                    res = proxyChain.doProxyChain();
                    after(proxyChain);
                } else {
                    res = proxyChain.doProxyChain();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
            return res;
        }

        protected void before(ProxyChain proxyChain) {

            System.out.println(this + "        ImplFirst before");
        }

        protected void after(ProxyChain proxyChain) {

            System.out.println(this + "        ImplFirst after");
        }

        private boolean intercept(Class cls, Method method, Object[] params) {
            return true;
        }

ProxyImplSecond

public class ProxyImplSecond extends ProxyImplFirst {
        @Override
        protected void before(ProxyChain proxyChain) {

            System.out.println(this + "                        Impl Second  before");
        }

        @Override
        protected void after(ProxyChain proxyChain) {

            System.out.println(this + "                        Impl Second  after");
        }
    }

TestMain

Main实现了ProxyInterface接口,这个接口只有一个SayHello方法。

  public class TestMain implements ProxyInterface {

        public static void main(String[] args) {
            List proxies = new ArrayList<>();
            for (int i = 0; i < 2; i++) {
                if (i % 2 == 0) {
                    Proxy proxy = new ProxyImplFirst();
                    proxies.add(proxy);
                } else {
                    Proxy proxy = new ProxyImplSecond();
                    proxies.add(proxy);
                }
            }
            TestMain instanceCG = new CgProxyManager().createProxy(TestMain.class, proxies);
            ProxyInterface instanceJDK = new JdkProxyManager().createProxy(TestMain.class, proxies);
            instanceCG.sayHello();
            System.out.println(" ");
            instanceJDK.sayHello();
        }

        @Override
        public void sayHello() {
            System.out.println(this.getClass().getSimpleName() + " ------------Hello-------- ");
        }
    }

注意Jdk的动态代理必须返回的接口ProxyInterface,否则会出现类型转换异常是,即将ProxyInterface instanceJDK = new JdkProxyManager().createProxy(TestMain.class, proxies);改为TestMain instanceJDK = new JdkProxyManager().createProxy(TestMain.class, proxies);会报错。

你可能感兴趣的:(Java)