一文掌握Java动态代理的奥秘与应用场景

本文介绍了动态代理的概念,其在SpringAOP、OpenFeign和Mybatis等场景中的应用,以及Java中JavaProxy、CGLIB、AspectJ和Instrumentation等动态代理实现方式。重点讲解了CGLIB动态代理的工作原理和ASM字节码操作框架的作用。
摘要由CSDN通过智能技术生成

一、基本概念

        为某个对象提供一个代理,以控制对这个对象的访问。代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象来替代。代理类负责请求的预处理、过滤、将请求分派委托类处理、以及委托类执行完请求后的后续处理。

        为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类的直接访问,也可以很好的保护和隐藏委托类对象,同时也为实现不同的策略预留了空间,从而在设计上获得了更大的灵活性。

        代理主要分为静态代理和动态代理:

  • 静态代理:创建代理类或特定工具自动生成源代码在对其编译,在程序运行前代理类的class文件就已经存在。
  • 动态代理:动态代理是一种编程技术,允许在运行时动态创建并处理代理对象,代理对象可以作为真实对象(也称为目标对象)的替代品,对真实对象的访问请求进行拦截、过滤、增强或修改。

        下面主要介绍一下动态代理技术

二、动态代理

        2.1 动态代理应用场景

        即使你不了解动态代理,但是相信是平时你已经在不经意间使用过动态代理,下面看下动态代理的使用场景。

  1. Spring AOP:AOP 使用了动态代理技术来实现对目标对象的代理和增强。
  2. SpringCloud OpenFeign:OpenFeign 中指定义了 FeignClient 的接口,但却能实现远程调用,OpenFeign 中只用了动态代理技术来实现远程调用。
  3. Mybatis 的接口绑定:定义的Mapper接口和xml文件,也是通过动态代理技术来实现的。
  4. RPC 框架:其实与 OpenFeign类似,比如 Dubbo、gRPC 等都是通过动态代理技术来实现远程调用的。

        上面这些功能相信你在平时都应用过,动态代理的应用场景不只列举的这些,应用十分广泛。

        2.2 动态代理的实现

        动态代理的代理实现方式有多种,在 Java 中主要有以下四种实现方式:

  • Java Proxy:是基于接口实现的,动态代理基于 java.lang.reflect.Proxy 类 java.lang.reflect.InvocationHandler 实现。代理类在运行时动态生成,动态的构建全新的字节码 Bean。
  • CGLIB:是一个三方库,动态构建全新的字节码Bean,不依赖接口。CGLIB通过生成目标类的子类来实现代理功能,因此即使目标类没有实现任何接口,也可以为其创建代理。
  • AspectJ:AspectJ 提供了一种更为强大且全面的切面编程解决方案,它既可以静态编译时织入(compile-time weaving),也可以在运行时织入(load-time weaving 或 runtime weaving)。修改目标类的字节,在程序编译时织入,不会生成全新的class,因此性能更好一些。
  • Instrumentation:是Java平台提供的一个内置 API,它主要用在 JVM 加载类的过程中进行字节码级别的操作,实现类的动态修改和增强,这与传统的动态代理机制(如Java的JDK动态代理和CGLIB动态代理)有所不同。不用再运行时创建新的class。

        动态代理技术的底层实现是修改字节码

三、使用案例

        下面演示一下 CGLIB 动态代理是如何工作的,加入我们有一个 HelloService 的类,要对台进行动态代理,在 sayHello 的执行前后分别做一些我们想做的事情。

public class HelloService {

    public HelloService() {
        System.out.println("HelloService构造");
    }

    /**
     * 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
     */
    final public String sayOthers(String name) {
        System.out.println("HelloService:sayOthers>>"+name);
        return null;
    }

    public void sayHello() {
        System.out.println("HelloService:sayHello");
    }

}
public class MyMethodInterceptor implements MethodInterceptor {

    /**
     *
     * @param sub cglib生成的代理对象
     * @param method 被代理对象的方法
     * @param objects 方法入参
     * @param methodProxy 代理方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======插入前置通知======");
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("======插入后者通知======");
        return object;
    }
}


public class Client {

    public static void main(String[] args) {
        // 代理类class文件存入本地磁盘方便我们反编译查看源码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(HelloService.class);
        // 设置enhancer的回调对象
        enhancer.setCallback(new MyMethodInterceptor());
        // 创建代理对象
        HelloService proxy = (HelloService) enhancer.create();
        // 通过代理对象调用目标方法
        proxy.sayHello();
    }
}

        运行代码就能看到动态代理的结果了。我们看到 CGLIB 等是依赖 ASM,那 ASM 又是什么呢?

        3.1 ASM 介绍

        ASM (Abstract Syntax Tree, bytecode manipulation and analysis framework) 是一个用于Java 字节码操作和分析的开源框架。ASM 允许开发者在运行时直接操作 Java 类的字节码,提供了低层次的 API,使得开发者可以精细地控制字节码的生成和修改,实现如字节码增强、类和方法的动态修改、AOP(面向切面编程)等功能。        

        ASM 在JDK 中,具体位置如图

        通过 ASM 我们可以获取指定类的字节码

public class AsmUtils {

    public static void main(String[] args) {
        try {
            ClassReader reader = new ClassReader("指定全路径");
            TraceClassVisitor visitor = new TraceClassVisitor(new PrintWriter(System.out));
            reader.accept(visitor, ClassReader.SKIP_FRAMES);
        } catch (Exception e) {

        }
    }
}

        ASM的特点如下:

  1. 高度灵活:ASM 提供了对字节码的直接访问,可以精确控制每个字节码指令,具有很高的灵活性和可定制性。
  2. 高性能:由于 ASM 直接操作字节码,性能优异,尤其适合需要高性能字节码操作的场合。
  3. 广泛使用:ASM 被许多著名的开源项目和框架所使用,如 Hibernate、CGLIB、Spring 等,用以实现 AOP、动态代理、字节码增强等功能。
  4. 支持广泛的 Java 版本:ASM 支持从 Java 1.0 到最新的 Java 版本,兼容性非常好。
  5. API设计:ASM 的API设计简洁明了,开发者可以直接操作 ClassVisitor、MethodVisitor 和FieldVisitor 等接口,来遍历、修改或生成字节码。

        通过ASM,开发者可以实现例如方法调用拦截、性能监控、字节码注入等高级功能,是Java字节码操作领域的重要工具。

        总结:动态代理技术因其灵活性和实用性,在现代软件开发中的应用非常广泛,几乎涵盖了从底层服务到上层业务逻辑的各个环节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超越不平凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值