当前位置: 首页 > news >正文

Java动态代理模式深度解析

1. 动态代理基础

1.1 核心组件

  • Proxy 类:动态生成代理对象的工厂类,核心方法为 newProxyInstance()

  • InvocationHandler 接口:代理逻辑的处理器,所有方法调用会转发到其 invoke() 方法。

1.2 实现步骤

  1. 定义接口:代理基于接口实现。

    public interface UserService {
        void addUser(String username);
    }
  2. 实现类(真实对象)

    public class UserServiceImpl implements UserService {
        public void addUser(String username) {
            System.out.println("添加用户: " + username);
        }
    }
  3. 实现 InvocationHandler

    public class LoggingHandler implements InvocationHandler {
        private final Object target;
        public LoggingHandler(Object target) { this.target = target; }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("方法调用前: " + method.getName());
            Object result = method.invoke(target, args);
            System.out.println("方法调用后");
            return result;
        }
    }
  4. 创建代理对象

    UserService realService = new UserServiceImpl();
    UserService proxy = (UserService) Proxy.newProxyInstance(
        realService.getClass().getClassLoader(),
        new Class[]{UserService.class},
        new LoggingHandler(realService)
    );
    proxy.addUser("Alice");

1.3 底层原理

  • 动态生成代理类:运行时生成 $ProxyN 类字节码。

  • 方法调用流程:代理类方法调用委托给 InvocationHandler

1.4 应用场景

  • 日志记录、性能监控、事务管理、权限控制等横切关注点。

  • RPC 框架中的远程调用封装。

1.5 优缺点

优点缺点
业务逻辑与切面逻辑解耦仅支持接口代理
无需为每个类编写静态代理反射调用存在性能开销

1.6 高级话题

  • CGLIB 代理:通过继承实现类代理(需引入 cglib 依赖)。

  • Lambda 简化:Java 8+ 使用 Lambda 表达式定义 InvocationHandler

    UserService proxy = (UserService) Proxy.newProxyInstance(
        loader,
        new Class[]{UserService.class},
        (p, method, args) -> {
            System.out.println("Lambda 代理逻辑");
            return method.invoke(target, args);
        }
    );

2. 动态代理在 Spring Boot 中的应用

2.1 核心应用场景

场景实现方式示例
事务管理@Transactional + TransactionInterceptor方法执行前后管理事务
AOP 切面逻辑@Aspect + Advisor日志、权限校验、性能监控
Spring Data JPA动态生成 Repository 实现类findByUsername 自动实现
全局异常处理@ControllerAdvice统一处理 Controller 层异常

2.2 实现机制

  • 代理方式选择

    • JDK 动态代理:默认代理接口(需实现接口)。

    • CGLIB 代理:代理类(无接口时使用),Spring Boot 2.x+ 默认启用。

  • 代理生成流程

    1. Bean 初始化时检测是否需要代理。

    2. 匹配切面规则(通过 Pointcut)。

    3. 生成代理对象并织入逻辑。

2.3 调试与优化

  • 查看代理类

    # JDK 代理
    -Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
    # CGLIB 代理
    -Dcglib.debugLocation=/tmp/cglib
  • 验证代理类型

    boolean isJdkProxy = Proxy.isProxyClass(bean.getClass());
    boolean isCglibProxy = bean.getClass().getName().contains("$$EnhancerBySpringCGLIB$$");

2.4 典型问题与解决方案

  • 自调用失效问题

    // 错误:同类方法直接调用,事务失效
    public void outerMethod() {
        innerMethod(); // 未通过代理调用
    }
    
    // 正确:通过代理对象调用
    @Autowired
    private UserService self; // 注入代理后的 Bean
    public void outerMethod() {
        self.innerMethod();
    }

3. 多 Service 动态代理实现方案

3.1 通用代理实现

  • 支持多个 Service 的 InvocationHandler

    public class LoggingHandler implements InvocationHandler {
        private final Object target;
        public LoggingHandler(Object target) { this.target = target; }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("[Log] 调用方法: " + method.getName());
            return method.invoke(target, args);
        }
    }
  • 代理工厂类

    public class ProxyFactory {
        public static <T> T createProxy(T target, Class<T> interfaceType) {
            return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                new Class<?>[]{interfaceType},
                new LoggingHandler(target)
            );
        }
    }

3.2 结合 Spring 容器

  • 配置类定义代理 Bean

    @Configuration
    public class ProxyConfig {
        @Bean
        public UserService userService() {
            return ProxyFactory.createProxy(new UserServiceImpl(), UserService.class);
        }
    
        @Bean
        public OrderService orderService() {
            return ProxyFactory.createProxy(new OrderServiceImpl(), OrderService.class);
        }
    }
  • 使用代理对象

    @Service
    public class AppService {
        @Autowired
        private UserService userService;
    
        @Autowired
        private OrderService orderService;
    
        public void execute() {
            userService.addUser("Alice");
            orderService.createOrder("ORDER_001");
        }
    }

3.3 完整代码示例

  • 接口定义

    public interface OrderService {
        void createOrder(String orderId);
    }
  • 实现类

    public class OrderServiceImpl implements OrderService {
        @Override
        public void createOrder(String orderId) {
            System.out.println("创建订单: " + orderId);
        }
    }
  • 运行示例

    public class Main {
        public static void main(String[] args) {
            UserService userProxy = ProxyFactory.createProxy(new UserServiceImpl(), UserService.class);
            OrderService orderProxy = ProxyFactory.createProxy(new OrderServiceImpl(), OrderService.class);
    
            userProxy.addUser("Bob");
            orderProxy.createOrder("ORDER_002");
        }
    }

4. 总结

  • 动态代理的价值:通过解耦核心逻辑与横切关注点,提升代码复用性和可维护性。

  • 在 Spring Boot 中的实践

    • 默认使用 CGLIB 代理,支持类代理。

    • 广泛应用于事务、AOP、数据访问等场景。

  • 多 Service 代理方案:通过工厂模式和 Spring 容器管理,实现统一代理逻辑。

JDK 动态代理和CGLIB 代理的区别

一、CGLIB 代理示例

1. 添加依赖

CGLIB 需要额外引入依赖(Spring Boot 默认已包含):

<!-- Maven 依赖 -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
2. 定义目标类(无需接口)
public class UserService {
    public void addUser(String username) {
        System.out.println("添加用户: " + username);
    }
}
3. 实现方法拦截器 MethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class LoggingInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("方法调用前: " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // 调用父类(原始类)方法
        System.out.println("方法调用后");
        return result;
    }
}
4. 生成 CGLIB 代理对象
import net.sf.cglib.proxy.Enhancer;

public class CglibProxyDemo {
    public static void main(String[] args) {
        // 创建增强器
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);          // 设置父类(目标类)
        enhancer.setCallback(new LoggingInterceptor());    // 设置回调(拦截器)

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

        // 调用方法
        proxy.addUser("Alice");
    }
}
5. 输出结果
方法调用前: addUser
添加用户: Alice
方法调用后

二、JDK 动态代理 vs CGLIB 代理对比

对比维度JDK 动态代理CGLIB 代理
实现机制基于接口,通过 Proxy 类生成代理对象基于继承,通过修改字节码生成目标类的子类作为代理
目标类要求必须实现至少一个接口可以代理无接口的类(通过继承)
性能生成代理对象较快,但反射调用方法较慢生成代理对象较慢(需操作字节码),但方法调用较快
方法覆盖只能代理接口中的方法可以代理目标类中所有非 final 方法
依赖关系无需额外依赖(JDK 内置)需要引入 cglib 库
应用场景Spring 中代理接口实现类(如 @RepositorySpring 中代理无接口的类(如 @Service@Controller
局限性无法代理未实现接口的类无法代理 final 类或 final 方法

三、关键区别详解

1. 实现原理
  • JDK 动态代理

    • 基于 Java 反射机制,运行时动态生成实现指定接口的代理类。

    • 代理类名格式:$ProxyN(如 $Proxy0)。

    // 生成的代理类伪代码
    public final class $Proxy0 extends Proxy implements UserService {
        public final void addUser(String username) {
            super.h.invoke(this, m3, new Object[]{username});
        }
    }
  • CGLIB 代理

    • 通过 ASM 字节码操作框架,生成目标类的子类作为代理。

    • 代理类名格式:TargetClass$$EnhancerByCGLIB$$...

    // 生成的代理类伪代码
    public class UserService$$EnhancerByCGLIB$$12345 extends UserService {
        private MethodInterceptor interceptor;
    
        public void addUser(String username) {
            interceptor.intercept(this, method, args, methodProxy);
        }
    }
2. 性能对比
操作JDK 动态代理CGLIB 代理
代理对象生成速度慢(需操作字节码)
方法调用速度较慢(反射调用)快(直接调用父类方法)
3. Spring 中的选择策略
  • Spring Boot 2.x+:默认使用 CGLIB 代理(通过 @EnableAspectJAutoProxy(proxyTargetClass = true))。

  • 强制使用 JDK 代理:若目标类实现了接口,可通过配置 proxyTargetClass = false 切换。

4. 代码示例对比
场景JDK 动态代理CGLIB 代理
目标类定义必须实现接口可以是普通类
代理对象创建Proxy.newProxyInstance()Enhancer.create()
方法调用入口InvocationHandler.invoke()MethodInterceptor.intercept()

四、如何选择代理方式?

  1. 有接口且需轻量级代理 → JDK 动态代理。

  2. 无接口或需代理类方法 → CGLIB 代理。

  3. 目标类有 final 修饰 → 无法使用 CGLIB,需改用 JDK 动态代理或重构代码。


五、总结

  • JDK 动态代理:轻量级接口代理方案,适合简单场景。

  • CGLIB 代理:功能更强大的类代理方案,适合复杂场景(无接口或需高性能调用)。

  • Spring 最佳实践:优先使用 CGLIB 代理,避免接口依赖问题(如 Spring Data JPA 的 Repository 实现)。

相关文章:

  • Git 分支删除操作指南(含本地与远程)
  • 如何将MediaPipe编译成Android中Chaquopy插件可用的 .whl 文件
  • 鸿蒙NEXT开发问题大全(不断更新中.....)
  • PyQt5库 各种导入项的作用
  • BUUCTF Pwn babyheap_0ctf_2017 Unsorted bin attack部分
  • Rust语言的物理引擎
  • 嵌入式硬件篇---PWM输出通道定时器
  • JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、复杂API请求、DOM操作、搜索和过滤等,array.map()的使用详解(附实际应用代码)
  • FreeRTOS移植并实现一个多任务程序
  • mysql中查询没有主键的表
  • 《Python深度学习》第五讲:文本处理中的深度学习
  • 一学就会:A*算法详细介绍(Python)
  • springcloud, nacos使用rabbitMq
  • 前端面试项目拷打
  • Django:内置和自定义中间件
  • Java集合的底层原理
  • 深入解析ES6+新语法:复杂的迭代器与生成器
  • 【css酷炫效果】纯CSS实现手风琴折叠效果
  • navicat导出文件密码解密
  • vue3二次封装tooltip实现el-table中的show-overflow-tooltip效果
  • 央行宣布优化两项支持资本市场的货币政策工具
  • 中演协:五一假期全国营业性演出票房收入同比增长3.6%
  • 特朗普要征100%关税,好莱坞这批境外摄制新片能躲过吗?
  • 世锦赛决赛今夜打响,斯诺克运动需要赵心童创造历史
  • 广西科学调度保障春灌面积1373.53万亩
  • 平安资管总经理罗水权因个人工作原因辞职