首页 > 技术文章 > 谈点动态代理

fengshenjingjun 2019-05-14 22:14 原文

  java里的动态代理一般有jdk的动态代理和cglib实现的动态代理两种,用起来也蛮好用的,也一直没有深究实现原理,今天心血来潮就看看他们的实现。

  jdk的动态代理类需要实现InvocationHandler这个接口,然后在invoke方法里调用被代理类的方法,方法前后可以做任何你想做的操作。调用时候用如下方式调用:

InvocationHandler handler = new MyInvocationHandler(...);
Class proxyClass = Proxy.getProxyClass(
         Foo.class.getClassLoader(), new Class[] { Foo.class });
     Foo f = (Foo) proxyClass.
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });

或者更简单的:

 Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class[] { Foo.class },
                                          handler);

可以看到jdk实现的动态代理,被代理类必须要实现接口,这是jdk和cglib实现动态代理的一个区别。那么它是怎么实现的呢,打开Proxy类的源码,查看newProxyInstance方法,可以看到它先通过final Class<?>[] intfs = interfaces.clone()获取到一个Class数组,然后调用getProxyClass0(loader, intfs);获取到一个Class,之后获取这个Class的构造器:final Constructor<?> cons = cl.getConstructor(constructorParams);最后return一个构造器构造出的实例cons.newInstance(new Object[]{h})。那么问题就在于这个获取到的Class是什么,其实在实现里头,把这个获取到的Class打印出来,会发现是这么一个玩意com.sun.proxy.$Proxy0,那么这是怎么来的呢,我们看getProxyClass0方法。

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

可以看到注释里写的,如果这个代理类存在缓存里,就从缓存里获取,否则通过ProxyClassFactory去创建一个,这个代码就不贴了,最后是通过一个本地方法生成的这个代理类。这个代理类大概长成这样:

public final class $Proxy0 extends Proxy implements ISubject {
    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 hello(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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.studytest.www.ISubject").getMethod("hello", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到它实现了Object类的toString,hashCode和equals方法,当调用我的hello方法的时候,会去调代理类的invoke方法。

  CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它通过继承方式实现代理。假设有个类HelloConcrete

public class HelloConcrete {
    public String sayHello(String str) {
        return "HelloConcrete: " + str;
    }
}

然后实现一个MyMethodInterceptor

public class MyMethodInterceptor implements MethodInterceptor {

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

最后来个测试

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloConcrete.class);
enhancer.setCallback(new MyMethodInterceptor());

HelloConcrete hello = (HelloConcrete)enhancer.create();
System.out.println(hello.getClass());
System.out.println(hello.sayHello("I love you!"));

动态代理就完成了,我们打印了这个代理生成的class,发现是HelloConcrete$$EnhancerByCGLIB$$c3066653,第一感觉就是跟jdk生成的那个代理类的名字风格差不多,把生成的类打印出来看

public class HelloConcrete$$EnhancerByCGLIB$$c3066653 extends HelloConcrete implements Factory {
...
public final boolean equals(Object var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
            return var2 == null ? false : (Boolean)var2;
        } else {
            return super.equals(var1);
        }
    }

    public final String toString() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString();
    }

    public final int hashCode() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
            return var1 == null ? 0 : ((Number)var1).intValue();
        } else {
            return super.hashCode();
        }
    }
...
public final String sayHello(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$sayHello$0$Method, new Object[]{var1}, CGLIB$sayHello$0$Proxy) : super.sayHello(var1);
    }

}

大致是这么个样子,其中省略了不少代码,当调用代理对象的sayHello()方法时,首先会尝试转发给MethodInterceptor.intercept()方法,如果没有MethodInterceptor就执行父类的sayHello()。它也实现了Object类的toString,hashCode和equals方法,而由于是通过继承实现,其他final的方法肯定就没法实现了。至于如何获取动态代理的生成类,jdk动态代理可以在生成代理类之前加上System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"),而CGLIB动态代理我是参考的https://blog.csdn.net/lzufeng/article/details/79322391。

推荐阅读