揭秘JDK动态代理

代理在我们日常开发过程中有着很重要的角色,它可以处理一些日志记录,权限控制,事务等。主要分为静态代理和动态代理,他们的主要区别是有没有源文件,静态代理是有源文件的,应用起来比较直观但是稍有浪费且不智能,所以我们一般使用的是动态代理,他是在程序运行期间动态生成的,程序加载动态生成的字节码生成Class对象加以运用,灵活性高。
下面我们介绍一下jdk的动态代理。
首先声明一个接口IHello.java

public interface IHello {
    void sayHello(String name);
}

在创建一个接口的具体的实现类HelloService.java

public class HelloService implements IHello {
    @Override
    public void sayHello(String name) {
        System.out.println("hello "+name);
    }
}

使用jdk的动态代理,代理的逻辑必须写在InvocationHandler的实现类中,当调用接口的具体方法时,实际上是调用的InvocationHandler中的invoke方法,这个在后面会有详细的解释。
HelloHandler.java

public class HelloHandler implements InvocationHandler {
    /**
     * 调用的具体的对象
     */
    private Object concreteObj;

    public HelloHandler(IHello hello) {
        concreteObj = hello;
    }

    /**
     * @param proxy   生成的代理对象的回填
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy:"+proxy.getClass().getName());
        long start = System.currentTimeMillis();
        Object o = method.invoke(concreteObj, args);
        System.out.println("cost time :" + (System.currentTimeMillis() - start) + "ms");
        return o;
    }
}

需要的类都准备好了,我们来测试一下

    public static void main(String[] args) {
        IHello hello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(), new Class[]{IHello.class}, new HelloHandler(new HelloService()));
        hello.sayHello("test");
    }

结果为:

proxy:com.sun.proxy.$Proxy0
hello test
cost time :0ms

我们这个动态代理的作用就是统计一下方法调用的时间。
我们发现了两个问题:

  1. 这个HelloHandler中invoke方法第一个参数proxy是怎么填入的并且他的class的name为何为com.sun.proxy.$Proxy0
  2. 我调用的是IHello中的sayHello方法,为啥他会走到HelloHandler中的invoke方法。

我们带着这两个疑问继续看起来。
我们先看一下Proxy是如何newProxyInstance的

揭秘JDK动态代理_第1张图片
110.png

下面我们看一下生成的字节码是怎样的

    public static void main(String[] args) {
         createProxyClassFile();
    }

    public static void createProxyClassFile() {
        String name = "ProxyHello";
        byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{IHello.class});
        try {
            FileOutputStream out = new FileOutputStream(name + ".class");
            out.write(data);
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

创建的文件这这样的

public final class ProxyHello extends Proxy implements IHello {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public ProxyHello(InvocationHandler var1) throws  {
        super(var1);
    }

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

    public final void sayHello(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 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 int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("com.pajk.homecare.unittest.proxy.IHello").getMethod("sayHello", new Class[]{Class.forName("java.lang.String")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

根据这个文件,我们看一下,当调用代理的sayHello方法时,调用的是handler的invoke方法,传入的第一个参数是this,所以就是Proxy的实例了。所以上面提出的两个问题得解。

你可能感兴趣的:(揭秘JDK动态代理)