郭卫东 发表于 前天 20:25

Spring Boot 项目中,JDK 动态代理和 CGLIB 动态代理的使用

在 Spring Boot 项目中,JDK 动态代理和 CGLIB 动态代理都是实现 AOP (面向切面编程) 的重要技能。 它们的重要区别在于代理对象的天生方式和适用范围。 下面详细先容它们的使用场景:
1. JDK 动态代理 (JDK Dynamic Proxy)


[*] 原理:

[*]JDK 动态代理是 Java 提供的内置代理机制,它通过反射在运行时动态地天生代理类。
[*]代理类会实现目标类实现的接口,并将接口中的方法调用委托给一个 InvocationHandler 对象来处理。
[*]InvocationHandler 接口负责实现详细的增强逻辑,例如日记纪录、安全检查、事件管理等。

[*] 使用场景:

[*]目标类实现了接口: 如果目标类实现了接口,则 Spring AOP 默认使用 JDK 动态代理。
[*]简单 AOP 场景: 适用于简单的 AOP 场景,例如只需要对接口方法进行增强的环境。
[*]不需要代理类的构造函数: JDK 动态代理创建代理对象时,不需要调用目标类的构造函数。

[*] 优点:

[*]简单易用: JDK 动态代理是 Java 内置的代理机制,使用起来比力简单。
[*]不需要第三方库: 不需要依赖第三方库。
[*]对接口友好: 代理类实现了目标类实现的接口,符合面向接口编程的头脑。

[*] 缺点:

[*]必须实现接口: 目标类必须实现接口,否则无法使用 JDK 动态代理。
[*]只能代理接口方法: 只能代理接口中定义的方法,无法代理类自身定义的方法。

[*] 示例代码:
// 接口
interface MyInterface {
    void doSomething();
}

// 实现类
class MyClass implements MyInterface {
    @Override
    public void doSomething() {
      System.out.println("MyClass is doing something...");
    }
}

// 调用处理器
class MyInvocationHandler implements InvocationHandler {
    private Object target; // 被代理的对象

    public MyInvocationHandler(Object target) {
      this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println("Before method: " + method.getName()); // 前置增强
      Object result = method.invoke(target, args); // 调用原始方法
      System.out.println("After method: " + method.getName()); // 后置增强
      return result;
    }
}

// 使用 JDK 动态代理
public class JDKDynamicProxyExample {
    public static void main(String[] args) {
      MyInterface target = new MyClass(); // 创建目标对象
      MyInvocationHandler handler = new MyInvocationHandler(target); // 创建调用处理器

      // 创建代理对象
      MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                MyInterface.class.getClassLoader(),
                new Class[] {MyInterface.class},
                handler
      );

      proxy.doSomething(); // 调用代理对象的方法,会被拦截
    }
}

2. CGLIB 动态代理 (CGLIB Dynamic Proxy)


[*] 原理:

[*]CGLIB (Code Generation Library) 是一个强大的、高性能的代码天生库。
[*]CGLIB 动态代理通过在运行时动态地天生目标类的子类来实现代理。
[*]代理类会继续目标类,并重写目标类的方法,在重写的方法中实现增强逻辑。
[*]CGLIB 动态代理不需要目标类实现接口,可以直接代理类。

[*] 使用场景:

[*]目标类没有实现接口: 如果目标类没有实现接口,则 Spring AOP 使用 CGLIB 动态代理。
[*]需要代理类自身定义的方法: 需要代理类自身定义的方法,而不但仅是接口方法。
[*]需要更高的性能: 在某些环境下,CGLIB 动态代理的性能可能比 JDK 动态代理更好。

[*] 优点:

[*]不需要实现接口: 目标类不需要实现接口,可以直接代理类。
[*]可以代理类自身定义的方法: 可以代理类自身定义的方法,而不但仅是接口方法。
[*]性能较好: 在某些环境下,CGLIB 动态代理的性能可能比 JDK 动态代理更好。

[*] 缺点:

[*]需要第三方库: 需要依赖 CGLIB 库。
[*]实现复杂: CGLIB 动态代理的实现比力复杂,需要天生目标类的子类。
[*]final 方法无法代理: 无法代理被 final 修饰的方法。
[*]对构造函数有要求: CGLIB 创建代理对象时,需要调用目标类的构造函数。 如果目标类没有无参构造函数,则需要手动指定构造函数。

[*] 示例代码:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 目标类
class MyClass {
    public void doSomething() {
      System.out.println("MyClass is doing something...");
    }
}

// 方法拦截器
class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
      System.out.println("Before method: " + method.getName()); // 前置增强
      Object result = proxy.invokeSuper(obj, args); // 调用原始方法
      System.out.println("After method: " + method.getName()); // 后置增强
      return result;
    }
}

// 使用 CGLIB 动态代理
public class CGLIBDynamicProxyExample {
    public static void main(String[] args) {
      Enhancer enhancer = new Enhancer(); // 创建 Enhancer 对象
      enhancer.setSuperclass(MyClass.class); // 设置超类
      enhancer.setCallback(new MyMethodInterceptor()); // 设置回调

      MyClass proxy = (MyClass) enhancer.create(); // 创建代理对象

      proxy.doSomething(); // 调用代理对象的方法,会被拦截
    }
}

3. Spring Boot 中的 AOP 配置
在 Spring Boot 项目中,可以通过以下方式配置 AOP:


[*] 添加 AOP 依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

[*] 编写切面类:
@Aspect
@Component
public class LoggingAspect {

    @Before("execution(* com.example.demo.service.*.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
      System.out.println("Before executing method: " + joinPoint.getSignature());
    }
}

[*] 配置代理方式:
默认环境下,Spring AOP 会根据目标类是否实现了接口来选择使用 JDK 动态代理或 CGLIB 动态代理。 可以通过 @EnableAspectJAutoProxy 注解的 proxyTargetClass 属性来强制使用 CGLIB 动态代理。
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用 CGLIB 动态代理
public class AopConfig {
    // ...
}

总结:
特性JDK 动态代理CGLIB 动态代理目标类要求必须实现接口不需要实现接口代理对象天生方式实现接口继续类性能一般较好易用性简单复杂是否需要第三方库否是 (net.sf.cglib)适用场景目标类实现了接口,简单 AOP 场景目标类没有实现接口,需要代理类自身定义的方法,性能要求较高@EnableAspectJAutoProxy默认值:falseproxyTargetClass = true 在实际开发中,Spring AOP 会自动选择符合的代理方式。 如果没有特殊需求,可以使用默认配置。 如果需要强制使用 CGLIB 动态代理,可以设置 @EnableAspectJAutoProxy(proxyTargetClass = true)。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Spring Boot 项目中,JDK 动态代理和 CGLIB 动态代理的使用