理解java动态代理

忘是亡心i 2021-09-23 03:54 425阅读 0赞

理解java动态代理

java动态代理主要用来做方法的增强,让你可以在不修改原来代码的情况下,增强一些方法,也就是在原来的方法执行前后做任何你想做的事情。具体应用的话,比如可以添加调用日志,做事务控制等。Spring AOP的底层原理就是利用了动态代理来实现的。
动态代理的实现一般有两种方式:

#

  • JDK动态代理,当目标类实现了接口,我们可以使用jdk的Proxy来生成代理对象
  • 使用CGLIB生成代理,CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。

本次先看看JDK动态代理如何实现,网上有很多的代码实例可参考。使用JDK动态代理前提是 目标类必须实现了某个接口

  • 接口

    public interface Subject {

    1. public void doSomething();

    }

  • 接口实现类

    public class RealSubject implements Subject {

    1. @Override
    2. public void doSomething() {
    3. System.out.println( "call doSomething()" );
    4. }

    }

  • 代理类的实现

    public class ProxyHandler implements InvocationHandler {

    1. private Object targetObject;
    2. public Object createProxyInstance(Object targetObject){
    3. this.targetObject = targetObject;
    4. /* * 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器 * 第二个参数设置代理类实现的接口 * 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法 */
    5. return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
    6. this.targetObject.getClass().getInterfaces(),
    7. this);
    8. }
    9. public Object invoke( Object realProxy, Method method, Object[] args ) throws Throwable
    10. {
    11. System.out.println(" param Object proxy is " + realProxy.getClass().getName());
    12. //在转调具体目标对象之前,可以执行一些功能处理
    13. before();
    14. //转调具体目标对象的方法
    15. Object obj = method.invoke(targetObject, args);
    16. //在转调具体目标对象之后,可以执行一些功能处理
    17. after();
    18. return obj;
    19. }
    20. public void before(){
    21. System.out.println("before method.invoke ...");
    22. }
    23. public void after(){
    24. System.out.println("after method.invoke ...");
    25. }

重点关注 createProxyInstance 方法,它返回真正的在内存中创建的代理类对象,具体查看反编译代码发现是如下形态:
public final class $Proxy0 extends Proxy implements Subject
后面分析这个createProxyInstance 方法返回的具体代理类。

  • 代理类测试

    public class DynamicProxy {

    1. public static void main(String[] args){
    2. // 被代理的实际对象
    3. Subject targetObject = new RealSubject();
    4. // handler对象作用是用于在运行时jvm创建实际代理类对象的依据
    5. ProxyHandler handler = new ProxyHandler();
    6. // 利用Proxy.newProxyInstance 创建一个代理类对象
    7. Subject proxySubject = (Subject) handler.createProxyInstance(targetObject);
    8. // [代理类对象] 执行 [被代理的实际对象] 的doSomething方法
    9. proxySubject.doSomething();
    10. // write proxySubject class binary data to file
    11. createProxyClassFile();
    12. }
    13. // 将真正的代理类以class字节码文件形式展现出来,就是内存中真正执行代理操作的类
    14. public static void createProxyClassFile()
    15. {
    16. String name = "ProxySubject";
    17. byte[] data = ProxyGenerator.generateProxyClass(name, RealSubject.class.getInterfaces());
    18. try
    19. {
    20. File file = new File("E:\\"+name + ".class");
    21. if (file.exists()){
    22. file.delete();
    23. }
    24. FileOutputStream out = new FileOutputStream(file);
    25. out.write( data );
    26. out.close();
    27. }
    28. catch( Exception e )
    29. {
    30. e.printStackTrace();
    31. }
    32. }

    }

  • 运行DynamicProxy 的 main 方法可在控制台看到如下信息
    param Object proxy is com.sun.proxy.$Proxy0
    before method.invoke …
    call doSomething()
    after method.invoke …

并且在E盘下生成一个class字节码文件 ProxySubject.class 它就是 proxy iscom.sun.proxy.$Proxy0,当然输出的时候修改它的名称为ProxySubject。

param Object proxy is com.sun.proxy.$Proxy0这行输出信息也表示 ProxyHandler类中的方法 public Object invoke( Object realProxy, Method method, Object[] args ) 的第一个参数 Object realProxy 其实就是运行期间jvm动态生成的代理类对象 $Proxy。

反编译代理类 $Proxy 即 刚刚E盘下生成的文件 ProxySubject.class 查看实际执行的代理类究竟是什么鬼。
部分编译代码如下:

  1. public final class ProxySubject extends Proxy implements Subject {
  2. private static Method m1;
  3. private static Method m3;
  4. private static Method m2;
  5. private static Method m0;
  6. public ProxySubject(InvocationHandler paramInvocationHandler)
  7. {
  8. super(paramInvocationHandler);
  9. }
  10. public final boolean equals(Object paramObject)
  11. {
  12. // .... 省略
  13. }
  14. public final void doSomething()
  15. {
  16. try
  17. {
  18. this.h.invoke(this, m3, null);
  19. return;
  20. }
  21. catch (Error|RuntimeException localError)
  22. {
  23. throw localError;
  24. }
  25. catch (Throwable localThrowable)
  26. {
  27. throw new UndeclaredThrowableException(localThrowable);
  28. }
  29. }
  30. public final String toString()
  31. {
  32. // .... 省略
  33. }
  34. public final int hashCode()
  35. {
  36. // .... 省略
  37. }
  38. static
  39. {
  40. try
  41. {
  42. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  43. m3 = Class.forName("cn.guzt.utils.Subject").getMethod("doSomething", new Class[0]);
  44. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  45. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  46. return;
  47. }
  48. catch (NoSuchMethodException localNoSuchMethodException)
  49. {
  50. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  51. }
  52. catch (ClassNotFoundException localClassNotFoundException)
  53. {
  54. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  55. }
  56. }
  57. }

注意上面的构造函数 中的参数paramInvocationHandler,其实我们创建的ProxyHandler对象,jvm创建动态代理类对象时自动将ProxyHandler对象传入。

该代理类 extends Proxy implements Subject,其中Proxy中含有 成员对象 protected InvocationHandler h; 而 此时 的h就是 paramInvocationHandler对象也即我们创建的ProxyHandler对象。

另外该代理类实现了Subject接口,所以我们看到测试类中DynamicProxy的代码:
Subject proxySubject = (Subject) handler.createProxyInstance(targetObject);
这下明白为什么handler.createProxyInstance(targetObject)可以被强制转换为Subject。

最后内存中创建的代理类执行 doSomething()方法时,跑到了 我们创建的ProxyHandler对象中执行invoke方法,invoke方法中的Method 参数就是被代理对象类中的doSomething()方法的反射。利用反射技术执行doSomething方法。

顺便说下 ProxyHandler中invoke方法的第一个参数之前说过了,它是我们实际代理类对象com.sun.proxy.$Proxy0 ,这个参数其实没有什么特别用处,只是我们反编译过来看看用的。

到此一个基本的代理流程完毕,其实我感觉 jvm动态代理方式的实现比较。。。不是那么让人顺畅的理解,你创建的public class ProxyHandler implements InvocationHandler 并不是真正的代理执行者,而是jvm根据你创建的ProxyHandler又在内存中创建了一个真正的代理执行者com.sun.proxy.$Proxy0 ,这个真正的代理执行者其实最后还是调用了你创建的ProxyHandler对象中的invoke方法,只不过com.sun.proxy.$Proxy0 的作用就是在其内部将需要反射 处理等杂活给做了。

发表评论

表情:
评论列表 (有 0 条评论,425人围观)

还没有评论,来说两句吧...

相关阅读

    相关 彻底理解Java动态代理

    代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问。 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念。 代理模式示例代码

    相关 理解java动态代理

    理解java动态代理 java动态代理主要用来做方法的增强,让你可以在不修改原来代码的情况下,增强一些方法,也就是在原来的方法执行前后做任何你想做的事情。具体应用的话,比