动态代理详解

jdk动态代理

实例:动态代理有两个对象,目标对象和代理对象。使用JDK动态代理,目标对象必须实现一个接口。

public class JdkDynamicProxyTest {

    interface IPerson {
        void say() ;
    }

    static class Person implements IPerson {
        @Override
        public void say() {
            System.out.println("hello world");
        }
    }

    static class JdkProxy implements InvocationHandler {

        //目标对象
        private Object target;

        //得到代理对象
        public Object getProxy(Object target) {
            this.target = target;
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), this);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("say before");
            method.invoke(target, args);
            System.out.println("say after");
            return null;
        }
    }

    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        JdkProxy jdkProxy = new JdkProxy();
        IPerson person = (IPerson) jdkProxy.getProxy(new Person());
        person.say();
    }
}

原理
System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);会在项目路径下生成一个$Proxy0.class文件,反编译这个文件,就能够明白JDK动态代理原理。

final class $Proxy0 extends Proxy implements IPerson {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void say() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.ming.aop.IPerson").getMethod("say");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

代理的大概结构包括4部分:
静态字段:被代理的接口所有方法都有一个对应的静态变量;
静态块:主要是通过反射初始化静态变量;
具体每个代理方法:逻辑都差不多就是 h.invoke,h是Proxy的属性。主要是调用我们定义好的invocatinoHandler逻辑,触发目标对象target上对应的方法;
类结构如下:

public class Proxy implements java.io.Serializable {
    protected InvocationHandler h;
}

构造函数:从这里传入我们InvocationHandler逻辑(如上的JdkProxy );构造函数在newProxyInstance方法中通过反射调用

public $Proxy0(InvocationHandler var1) throws  {
    super(var1);
}

protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    this.h = h;
}

cglib动态代理

cglib使用需要实现MethodInterceptor接口,与JDK动态代理不同的是,cglib不需要目标类实现接口。

public class CglibDynamicProxyTest {

    static class Person {
        public void say() {
            System.out.println("hello world");
        }
    }

    static class CglibProxy implements MethodInterceptor {

        Object target;

        public Object getProxy(Object target) {
            this.target = target;
            final Enhancer enhancer = new Enhancer();
            enhancer.setUseFactory(false);
            enhancer.setSuperclass(target.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("say before");
            methodProxy.invokeSuper(o, objects);
            System.out.println("say after");
            return null;
        }
    }

    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G:\\gitRepository\\demo");
        CglibProxy cglibProxy = new CglibProxy();
        Person person = (Person) cglibProxy.getProxy(new Person());
        person.say();
    }
}

原理
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,“G:\gitRepository\demo”);会在配置的路径下会生成三个字节码文件,其中有两个继承FastClass类的是索引文件,还有一个继承person对象,是我们要研究的。

public class CglibDynamicProxyTest$Person$$EnhancerByCGLIB$$681783b8 extends Person {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$say$0$Method;
    private static final MethodProxy CGLIB$say$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.ming.test.CglibDynamicProxyTest$Person$$EnhancerByCGLIB$$681783b8");
        Class var1;
        CGLIB$say$0$Method = ReflectUtils.findMethods(new String[]{"say", "()V"}, (var1 = Class.forName("com.ming.test.CglibDynamicProxyTest$Person")).getDeclaredMethods())[0];
        CGLIB$say$0$Proxy = MethodProxy.create(var1, var0, "()V", "say", "CGLIB$say$0");
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
    }

    final void CGLIB$say$0() {
        super.say();
    }

    public final void say() {
        //1. 得到MethodInterceptor,我们实现的接口,通过enhancer.setCallback(this)设置的。
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        //2. 调用代理的逻辑
        if (var10000 != null) {
            var10000.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
        } else {
            super.say();
        }
    }
    //..省略一些方法
    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        CglibDynamicProxyTest$Person$$EnhancerByCGLIB$$681783b8 var1 = (CglibDynamicProxyTest$Person$$EnhancerByCGLIB$$681783b8)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (CGLIB$STATIC_CALLBACKS == null) {
                    return;
                }
            }

            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }

    }

    static {
        CGLIB$STATICHOOK1();
    }
}

代理类继承了目标类,并重写目标类方法。代理的大概结构包括三大块:

静态字段:目标类中所有方法(包括父类中的方法,Object除外)以及Object中finalize、equals、toString、hashCode、clone方法,都对应一个Method和MethodProxy静态变量。
静态代码块:静态代码块中调用CGLIB$STATICHOOK1()静态方法,这个方法主要是通过反射初始化Method静态变量,并初始化MethodProxy静态变量。Method表示目标类的方法,MethodProxy表示代理类中方法。
目标类中的方法:代理类中有两个方法,一个是重写目标类方法,重写的逻辑也比较简单,得到MethodInterceptor对象,然后调用intercept方法,intercept方法是我们实现的方法。还有一个CGLIBsay0方法,这个是给MethodProxy调用,实现逻辑直接调用目标类方法,在调用methodProxy.invokeSuper(o,
objects)时会调用这个方法。

注意如下的代码会发生了死循环。

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    System.out.println("say before");
    method.invoke(o, objects);
    System.out.println("say after");
    return null;
}

我们从反编译的代码中var10000.intercept(this, CGLIB$say 0 0 0Method, CGLIB e m p t y A r g s , C G L I B emptyArgs, CGLIB emptyArgs,CGLIBsay 0 0 0Proxy)中可以看出,o表示的是代理类,method表示的是目标类中方法,但是因为代理类重写了目标类中方法,所以还是调用代理类中的方法。代理的方法又会调用intercept,就形成了死循环。

jdk动态代理类与cglib代理区别

jdk动态代理目标类必须实现一个接口,而cglib不需要。是因为jdk代理会继承Proxy类,而java又是单继承。
jdk代理类与目标类实现相同的接口,cglib代理直接继承目标类。
jdk和cglib都是把实现逻辑代理给接口。jdk代理给InvocationHandler接口,cglib代理给MethodInterceptor接口。
cglib一个目标类方法会生成两个代理方法,一个重写目标方法,并实现代理逻辑,还有一个直接调用目标类方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值