揭秘JDK动态代理

作者: 数齐 | 来源:发表于2017-02-07 10:33 被阅读117次

代理在我们日常开发过程中有着很重要的角色,它可以处理一些日志记录,权限控制,事务等。主要分为静态代理和动态代理,他们的主要区别是有没有源文件,静态代理是有源文件的,应用起来比较直观但是稍有浪费且不智能,所以我们一般使用的是动态代理,他是在程序运行期间动态生成的,程序加载动态生成的字节码生成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的

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动态代理, CGLIB动态代理, SpringAOP动态代理 一,JDK动态代理  jdk动态代理...

  • 揭秘JDK动态代理

    代理在我们日常开发过程中有着很重要的角色,它可以处理一些日志记录,权限控制,事务等。主要分为静态代理和动态代理,他...

  • 动态代理

    Jdk 动态代理 Jdk动态代理,利用反射,实现 InvocationHandler 接口。Jdk 动态代理需要实...

  • JDK动态代理实践与原理

    本篇内容 介绍JDK动态代理的基本概念 一些JDK动态代理的疑问 JDK动态代理的Demo JDK动态代理的原理 ...

  • Spring系列之AOP(1)—— JDK动态代理和CigLib

    1、JDK动态代理 JDK动态代理通过JDK的Proxy类实现,JDK提供的动态代理需要实现InvocationH...

  • Spring的AOP原理分析

    一 动态代理 动态代理分为JDK动态代理和CGLIB动态代理 jdk动态代理 被代理类(目标类)和代理类必须实现同...

  • java面试宝典 五分钟了解spring代理 @Transact

    spring代理分为jdk动态代理和cglib代理 jdk动态代理 jdk动态代理是利用反射机制生成一个实现代理接...

  • 动态代理的两种方式

    静态代理就不说了,基本用到的都是动态代理。 Java中动态代理有JDK动态代理和CGLIB动态代理。 JDK代理的...

  • Java代理(jdk静态代理、动态代理和cglib动态代理)

    jdk静态代理 jdk动态代理 jdk动态代理的业务类必须实现接口 业务类的实现类 代理类的生成 cglib动态代...

  • spring aop

    JDK动态代理和CGLib代理 JDK的代理代理类 被代理接口 被代理实现类 启动类: jdk的动态代理是针对接口...

网友评论

    本文标题:揭秘JDK动态代理

    本文链接:https://www.haomeiwen.com/subject/kbrpittx.html