JDK 动态代理

jdk 提供了动态代理的 API:

Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class[]{Interface.class}, new DynamicProxyHandler(targetObject));
  • targetObject 为要被代理的目标对象
  • proxy 为目标对象的代理对象
  • DynamicProxyHandler 为 InvocationHandler 接口的实现类

如下:

class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    public DynamicProxyHandler(final Object proxied) {
        this.proxied = proxied;
    }

    @Override
    public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
        System.out.println("proxy: " + proxy.getClass().getName() + ", method:" + method.getName() + ", args: " + args);
        return method.invoke(proxied, args);
    }
}

当目标对象的方法被调用时,会先调用到 InvocationHandler 的 invoke 方法中,我们可以在这里对原有方法进行增强等逻辑.

CGLib 动态代理

参考文档

CGLib (Code Generation Library) 是一个字节码工具库,被用在很多的 Java 框架里,如 Hibernate 或者 Spring,
该字节码工具允许在程序的编译阶段之后操作和创建 class.

CGLib - 字节码生成库是一个生成和转换字节码的高级APi, 它被用于 AOP, testing,data access frameworks 来生成动态代理对象
以及拦截字段访问.

CGLib 和 ASM 字节码库之间的关系如下:

2023-03-29T08:07:27.png

CGLib 库组成方式如下:

  • core: low-level 的字节码操作类,大部分都与 ASM 有关.
  • transform: 在运行时或构建时用于类文件转换的类.
  • proxy: 用于代理的创建及方法拦截的类.
  • reflect: 用于更快反射和 C# 样式委托的类
  • util: 工具类
  • beans: JavaBean 相关的工具

官方文档对其的描述是:

本质上,它动态生成一个子类来复写被代理对象的 non-final 方法,并且连接到用于回调到用户定义的拦截器的勾子.
它比JDK 动态代理的方法更快.

常用方法:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(final Object o, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable {

        if (method.getName().equals("sayHello")) {
            return "hello, kongkong, my kong";
        }

        return proxy.invokeSuper(o, args);
    }
});

PersonService personService = (PersonService) enhancer.create();
String ret = personService.sayHello();

CGLib还有更多强大的功能,如,Bean Creator, Bean Copier 等,大家感兴趣可以参考上面的文档.

对比

  1. JDK 代理只能对接口的实现类生成代理,而CGLib不需要
  2. CGLib 不能代理 final 修饰的类(因为CGLib原理是继承,final的类是无法继承的)
  3. 效率方面:
    ----创建代理对象的效率: JDK代理 > CGLib
    ----执行效率:JDK代理 < CGLib
    因为 JDK代理是通过反射机制实现,而 CGLib使用字节码处理框架,通过修改字节码生成子类.
文章目录