Unique's Blog

Java动态代理

2022-11-11 · 1154字 · 6 min read
🏷️  Java

来源:https://www.jianshu.com/p/992dfcecff34
github:https://github.com/imhuster/DynamicProxy/blob/master/src/main/java/com/iqts/MyProxy.java

JDK 动态代理

实现:动态生成的代理类的字节码(内存中 []byte),原理是继承Proxy 类,实现被代理类的所有接口生成动态代理类

生成的代理类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy implements Subject {
    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 doOperation() 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("proxy.jdk.Subject").getMethod("doOperation");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

除了实现了被代理类所有接口中的方法外,JDK 动态代理还重写了 Object 类中的 hashCodeequalstoString 三个方法。(注意在 invoke 方法中在代理类上调用它们会递归)

  • 继承的 Proxy 类拥有 InvocationHandler 字段,通过代理类的构造器初始化;

  • 重写的方法中,都会转向调用 super.h.invoke

问题:

  1. 为什么 JDK 实现动态代理必须要求被代理类实现接口

动态生成的代理类需要继承 Proxy 类,而 Java 中只能单继承的限制使得被代理类必须实现接口才能实现动态代理
如果被代理的类没有实现接口就无法实现动态代理,这时候我们就需要使用第三方工具。

CGLib

CGLib 可以在运行期扩展 Java 类及实现Java接口、提供方法的拦截,因此被众多 AOP 框架使用。CGLib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类。

原理:CGLib 是通过继承被代理类实现动态代理的,这也就要求被代理的类不能是 final 类

代理类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package proxy.cglib;
...
public class RealSubject$$EnhancerByCGLIB$$5fe030cf extends RealSubject implements Factory {
    private MethodInterceptor CGLIB$CALLBACK_0;
    
    private static final Method CGLIB$doOperation$0$Method;
    private static final MethodProxy CGLIB$doOperation$0$Proxy;
    ...

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("proxy.cglib.RealSubject$$EnhancerByCGLIB$$5fe030cf");
        Class var1;
        CGLIB$doOperation$0$Method = ReflectUtils.findMethods(new String[]{"doOperation", "()V"}, (var1 = Class.forName("proxy.cglib.RealSubject")).getDeclaredMethods())[0];
        CGLIB$doOperation$0$Proxy = MethodProxy.create(var1, var0, "()V", "doOperation", "CGLIB$doOperation$0");
        ...
    }

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

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

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$doOperation$0$Method, CGLIB$emptyArgs, CGLIB$doOperation$0$Proxy);
        } else {
            super.doOperation();
        }
    }
    ......
    static {
        CGLIB$STATICHOOK1();
    }
}
  • CGLib 不仅重写了 Object 类的 hashCodeequalstoString方法,还重写了 clone 方法

  • 代理的方法都对应两个具体的方法,例如 CGLIB$doOperation$0doOperation ,后者是对被代理对象的重写方法,前者是调用父类的方法。因此在实现 MethodInterceptor 接口的 intercept 方法中:

    • methodProxy.invoke() 是对 doOperation 方法的调用(对于代理对象,就调用该重写方法,因此需要注意递归调用)
    • methodProxy.invokeSuper() 是对 CGLIB$doOperation$0 方法的调用(必须是代理对象,该方法仅调用父类的 doOperation 方法,即目标对象的方法)。
  • 由于是继承实现,final 方法是不会代理的,只会调用父类的 final 方法。

  • CGLib 可以直接生成接口的代理对象,代理对重新接口中的方法,类似 JDK 动态代理;
    ^26a5aa

  • CGLib 可以实现 MethodInterceptor 接口与 InvocationHandler 接口(CGLib包中),使用 InvocationHandler 方式与 JDK 方式类似,并且代理的方法只对应一个具体的方法(代理类对父类的重写方法-同名)。

本文链接: Java动态代理

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

发布日期: 2022-11-11

最新构建: 2024-12-26

本文已被阅读 0 次,该数据仅供参考

欢迎任何与文章内容相关并保持尊重的评论😊 !

共 43 篇文章 | Powered by Gridea | RSS
©2020-2024 Nuo. All rights reserved.