Java 代理模式与 AOP 底层

一、代理模式核心概念

1. 定义

代理模式是一种结构型设计模式,核心思想是通过一个 "代理类" 为目标类提供间接访问,在不修改目标类代码的前提下,对目标类的功能进行增强(如日志记录、权限校验、性能监控等)。

2. 核心角色

表格

角色 职责
抽象主题(Subject) 定义目标类和代理类的公共接口,确保代理类与目标类行为一致
目标对象(RealSubject) 真正执行业务逻辑的类,是代理的目标
代理对象(Proxy) 实现抽象主题接口,持有目标对象引用,在调用目标方法前后添加增强逻辑

3. 代理模式的价值

  • 解耦:业务逻辑与非业务逻辑(如日志、事务)分离;
  • 增强:无侵入式扩展目标类功能;
  • 控制:可控制对目标对象的访问(如权限校验);
  • 是 AOP(面向切面编程)的底层实现核心。

二、静态代理

1. 实现步骤

步骤 1:定义抽象主题接口
复制代码
// 抽象主题:订单服务接口
public interface OrderService {
    // 创建订单
    void createOrder(String orderNo);
    // 取消订单
    void cancelOrder(String orderNo);
}
步骤 2:实现目标类(真实主题)
复制代码
// 目标对象:订单服务实现类
public class OrderServiceImpl implements OrderService {
    @Override
    public void createOrder(String orderNo) {
        System.out.println("【目标类】创建订单:" + orderNo);
    }

    @Override
    public void cancelOrder(String orderNo) {
        System.out.println("【目标类】取消订单:" + orderNo);
    }
}
步骤 3:实现静态代理类
复制代码
// 静态代理类:增强订单服务的功能
public class OrderServiceStaticProxy implements OrderService {
    // 持有目标对象引用
    private final OrderService target;

    // 通过构造器注入目标对象
    public OrderServiceStaticProxy(OrderService target) {
        this.target = target;
    }

    @Override
    public void createOrder(String orderNo) {
        // 前置增强:日志记录 + 权限校验
        before("createOrder", orderNo);
        // 调用目标方法
        target.createOrder(orderNo);
        // 后置增强:记录耗时
        after("createOrder");
    }

    @Override
    public void cancelOrder(String orderNo) {
        before("cancelOrder", orderNo);
        target.cancelOrder(orderNo);
        after("cancelOrder");
    }

    // 前置增强逻辑
    private void before(String methodName, String orderNo) {
        System.out.println("【静态代理】前置增强:调用" + methodName + "方法,参数:" + orderNo);
        System.out.println("【静态代理】权限校验通过");
    }

    // 后置增强逻辑
    private void after(String methodName) {
        System.out.println("【静态代理】后置增强:" + methodName + "方法执行完成");
    }
}
步骤 4:测试静态代理
复制代码
public class StaticProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标对象
        OrderService target = new OrderServiceImpl();
        // 2. 创建代理对象
        OrderService proxy = new OrderServiceStaticProxy(target);
        // 3. 通过代理对象调用方法
        proxy.createOrder("ORDER_001");
        System.out.println("------------------------");
        proxy.cancelOrder("ORDER_001");
    }
}
输出结果
复制代码
【静态代理】前置增强:调用createOrder方法,参数:ORDER_001
【静态代理】权限校验通过
【目标类】创建订单:ORDER_001
【静态代理】后置增强:createOrder方法执行完成
------------------------
【静态代理】前置增强:调用cancelOrder方法,参数:ORDER_001
【静态代理】权限校验通过
【目标类】取消订单:ORDER_001
【静态代理】后置增强:cancelOrder方法执行完成

2. 静态代理的优缺点

优点 缺点
实现简单,易于理解和调试 代理类与目标类一一对应,类数量爆炸(维护成本高)
无侵入式增强目标类功能 接口变更时,代理类和目标类需同步修改
逻辑清晰,增强代码集中 无法动态扩展,新增目标类需手动编写代理类

三、JDK 动态代理

1. 核心原理

JDK 动态代理是 JDK 原生支持的代理方式,基于接口实现

  • 无需手动编写代理类,运行时通过 java.lang.reflect.Proxy 动态生成代理类字节码;
  • 核心依赖 InvocationHandler 接口,统一处理代理对象的方法调用;
  • 要求目标类必须实现至少一个接口(否则无法使用)。

2. 核心类 / 接口

类 / 接口 核心作用
Proxy 生成动态代理类的核心类,核心方法:newProxyInstance()
InvocationHandler 代理逻辑处理器,通过 invoke() 方法统一处理所有代理方法的增强逻辑

3. 实现步骤

步骤 1:复用抽象主题和目标类(同静态代理的 OrderService/OrderServiceImpl)
步骤 2:实现 InvocationHandler 接口
复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// JDK动态代理处理器:统一处理所有代理方法的增强逻辑
public class JdkDynamicProxyHandler implements InvocationHandler {
    // 目标对象(通用类型,适配所有接口)
    private final Object target;

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

    /**
     * 代理方法的核心处理逻辑
     * @param proxy  生成的代理对象(一般不用)
     * @param method 被调用的目标方法
     * @param args   方法入参
     * @return 目标方法的返回值
     * @throws Throwable 方法执行异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置增强
        before(method.getName(), args);
        // 执行目标方法
        Object result = method.invoke(target, args);
        // 后置增强
        after(method.getName());
        return result;
    }

    // 通用前置增强逻辑
    private void before(String methodName, Object[] args) {
        System.out.println("【JDK动态代理】前置增强:调用" + methodName + "方法,参数:" + (args != null ? args[0] : "无"));
        System.out.println("【JDK动态代理】权限校验通过");
    }

    // 通用后置增强逻辑
    private void after(String methodName) {
        System.out.println("【JDK动态代理】后置增强:" + methodName + "方法执行完成");
    }
}
步骤 3:生成动态代理对象并测试
复制代码
import java.lang.reflect.Proxy;

public class JdkDynamicProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标对象
        OrderService target = new OrderServiceImpl();
        
        // 2. 创建代理处理器
        InvocationHandler handler = new JdkDynamicProxyHandler(target);
        
        // 3. 生成动态代理对象(核心方法)
        OrderService proxy = (OrderService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 目标类类加载器
                target.getClass().getInterfaces(),  // 目标类实现的接口
                handler                              // 代理处理器
        );
        
        // 4. 通过代理对象调用方法
        proxy.createOrder("ORDER_002");
        System.out.println("------------------------");
        proxy.cancelOrder("ORDER_002");
        
        // 打印代理类的类名(验证动态生成)
        System.out.println("------------------------");
        System.out.println("代理类的类名:" + proxy.getClass().getName());
    }
}
输出结果
复制代码
【JDK动态代理】前置增强:调用createOrder方法,参数:ORDER_002
【JDK动态代理】权限校验通过
【目标类】创建订单:ORDER_002
【JDK动态代理】后置增强:createOrder方法执行完成
------------------------
【JDK动态代理】前置增强:调用cancelOrder方法,参数:ORDER_002
【JDK动态代理】权限校验通过
【目标类】取消订单:ORDER_002
【JDK动态代理】后置增强:cancelOrder方法执行完成
------------------------
代理类的类名:com.sun.proxy.$Proxy0

4. JDK 动态代理的优缺点

优点 缺点
无需手动编写代理类,灵活扩展 目标类必须实现接口(局限性)
代理逻辑统一,维护成本低 仅能代理接口中定义的方法,无法代理类方法
JDK 原生支持,无需额外依赖 底层基于反射,性能略低于 CGLIB

四、CGLIB 动态代理

1. 核心原理

CGLIB(Code Generation Library)是第三方字节码生成库,基于继承实现

  • 无需目标类实现接口,通过继承目标类动态生成子类作为代理类;
  • 底层通过 ASM 框架操作字节码,直接生成新的类文件;
  • Spring、MyBatis 等框架均内置 CGLIB 依赖。

2. 前置条件

  • 引入 CGLIB 依赖(Spring 项目无需手动引入,单独使用需添加):

    <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>

3. 核心类 / 接口

类 / 接口 核心作用
Enhancer CGLIB 核心类,用于生成代理类,类似 JDK 的 Proxy
MethodInterceptor 方法拦截器,替代 JDK 的 InvocationHandler,通过 intercept() 处理增强
MethodProxy 目标方法的代理对象,可优化方法调用性能(避免反射)

4. 实现步骤

步骤 1:创建无接口的目标类(CGLIB 适配无接口场景)
复制代码
// 无接口的目标类:用户服务
public class UserService {
    public void addUser(String username) {
        System.out.println("【目标类】添加用户:" + username);
    }

    public void deleteUser(String username) {
        System.out.println("【目标类】删除用户:" + username);
    }
    
    // 注意:final方法无法被CGLIB代理(子类无法重写)
    public final void queryUser(String username) {
        System.out.println("【目标类】查询用户:" + username);
    }
}
步骤 2:实现 MethodInterceptor 接口
复制代码
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// CGLIB方法拦截器:处理代理增强逻辑
public class CglibMethodInterceptor implements MethodInterceptor {
    // 目标对象
    private final Object target;

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

    /**
     * 拦截目标方法调用
     * @param proxy       代理对象(子类实例)
     * @param method      目标方法
     * @param args        方法入参
     * @param methodProxy 方法代理对象(优化调用)
     * @return 目标方法返回值
     * @throws Throwable 执行异常
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 前置增强
        before(method.getName(), args);
        // 执行目标方法(两种方式:反射 / methodProxy.invokeSuper)
        Object result = methodProxy.invokeSuper(proxy, args); // 推荐:性能更高
        // Object result = method.invoke(target, args); // 反射方式
        // 后置增强
        after(method.getName());
        return result;
    }

    private void before(String methodName, Object[] args) {
        System.out.println("【CGLIB代理】前置增强:调用" + methodName + "方法,参数:" + (args != null ? args[0] : "无"));
        System.out.println("【CGLIB代理】参数校验通过");
    }

    private void after(String methodName) {
        System.out.println("【CGLIB代理】后置增强:" + methodName + "方法执行完成");
    }
}
步骤 3:生成 CGLIB 代理对象并测试
复制代码
import net.sf.cglib.proxy.Enhancer;

public class CglibProxyTest {
    public static void main(String[] args) {
        // 1. 创建目标对象
        UserService target = new UserService();
        
        // 2. 创建CGLIB增强器
        Enhancer enhancer = new Enhancer();
        // 设置父类(目标类)
        enhancer.setSuperclass(UserService.class);
        // 设置方法拦截器
        enhancer.setCallback(new CglibMethodInterceptor(target));
        
        // 3. 生成代理对象(子类实例)
        UserService proxy = (UserService) enhancer.create();
        
        // 4. 调用代理方法
        proxy.addUser("张三");
        System.out.println("------------------------");
        proxy.deleteUser("张三");
        System.out.println("------------------------");
        proxy.queryUser("张三"); // final方法:无增强逻辑,直接执行目标方法
        
        // 打印代理类的类名
        System.out.println("------------------------");
        System.out.println("代理类的类名:" + proxy.getClass().getName());
    }
}
输出结果
复制代码
【CGLIB代理】前置增强:调用addUser方法,参数:张三
【CGLIB代理】参数校验通过
【目标类】添加用户:张三
【CGLIB代理】后置增强:addUser方法执行完成
------------------------
【CGLIB代理】前置增强:调用deleteUser方法,参数:张三
【CGLIB代理】参数校验通过
【目标类】删除用户:张三
【CGLIB代理】后置增强:deleteUser方法执行完成
------------------------
【目标类】查询用户:张三
------------------------
代理类的类名:com.example.proxy.UserService$$EnhancerByCGLIB$$xxxxxxx

5. CGLIB 代理的注意事项

  1. final 方法无法被代理:CGLIB 基于继承,子类无法重写 final 方法;
  2. 目标类不能是 final 类:final 类无法被继承,无法生成代理子类;
  3. 性能优于 JDK 动态代理:ASM 直接操作字节码,避免反射开销;
  4. 构造方法会被调用两次:生成代理类时会先创建目标类实例,需注意无参构造的影响。

6. CGLIB 与 JDK 动态代理对比

特性 JDK 动态代理 CGLIB 动态代理
实现方式 基于接口(实现接口) 基于继承(继承目标类)
目标类要求 必须实现接口 无需实现接口(不能是 final)
核心类 Proxy、InvocationHandler Enhancer、MethodInterceptor
性能 反射调用,性能略低 字节码生成,性能更高
适用场景 目标类有接口 目标类无接口

五、代理模式与 AOP 的关系

1. AOP 核心概念

AOP(面向切面编程)是一种编程思想,核心是将非业务逻辑(如日志、事务、权限)从业务逻辑中抽离,形成 "切面",并动态织入到业务方法中

2. AOP 与代理模式的映射

AOP 概念 代理模式对应角色
切面(Aspect) 代理类中的增强逻辑(before/after)
连接点(JoinPoint) 目标类的所有方法
切入点(Pointcut) 被增强的具体方法
通知(Advice) 前置 / 后置 / 环绕等增强逻辑
目标对象(Target) 代理模式中的目标对象
代理对象(Proxy) 代理模式中的代理对象

3. Spring AOP 的代理选择策略

Spring AOP 底层默认使用动态代理,选择规则:

  1. 如果目标类实现了接口:优先使用 JDK 动态代理;
  2. 如果目标类未实现接口:使用 CGLIB 动态代理;
  3. 可通过配置 spring.aop.proxy-target-class=true 强制使用 CGLIB。

六、实战案例:基于代理实现统一日志切面

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.time.LocalDateTime;

// 通用日志代理处理器
public class LogProxyHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 前置日志:记录方法调用信息
        String log = String.format(
                "[%s] 调用方法:%s.%s,参数:%s,调用者:%s",
                LocalDateTime.now(),
                target.getClass().getName(),
                method.getName(),
                args == null ? "无" : String.join(",", args.toString()),
                Thread.currentThread().getName()
        );
        System.out.println("【日志切面】" + log);

        // 2. 执行目标方法(处理异常)
        Object result = null;
        try {
            result = method.invoke(target, args);
            // 3. 后置日志:记录方法成功执行
            System.out.println("【日志切面】方法执行成功,返回值:" + result);
        } catch (Exception e) {
            // 4. 异常日志:记录方法执行异常
            System.out.println("【日志切面】方法执行异常:" + e.getMessage());
            throw e; // 抛出异常,不影响业务逻辑
        } finally {
            // 5. 最终日志:记录方法执行完成
            System.out.println("【日志切面】方法执行完成");
        }
        return result;
    }

    // 通用代理生成方法
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new LogProxyHandler(target)
        );
    }
}

// 测试:为任意业务类添加日志增强
public class LogProxyTest {
    public static void main(String[] args) {
        // 1. 业务类(实现接口)
        OrderService orderService = LogProxyHandler.createProxy(new OrderServiceImpl());
        orderService.createOrder("ORDER_003");

        // 2. 若业务类无接口,可替换为CGLIB实现
        // UserService userService = CglibLogProxy.createProxy(new UserService());
        // userService.addUser("李四");
    }
}

总结

  1. 代理模式分为静态代理和动态代理:静态代理手动编写代理类,适用于简单场景;动态代理(JDK/CGLIB)运行时生成代理类,是 AOP 核心;
  2. JDK 与 CGLIB 代理的核心区别:JDK 基于接口,CGLIB 基于继承,Spring AOP 会自动选择适配方式;
  3. 代理模式是 AOP 的底层实现:通过代理类将切面逻辑(日志、事务等)织入目标方法,实现业务与非业务逻辑的解耦。
相关推荐
小鸡吃米…2 小时前
Python 网络爬虫
开发语言·爬虫·python
weixin_404157682 小时前
Java高级面试与工程实践问题集(二)
java·开发语言·面试
渔民小镇2 小时前
不止 request/response —— ionet 的 4 种通信模型选型指南
java·服务器·游戏
金蕊泛流霞2 小时前
Spring AI Alibaba笔记
java·笔记·spring
落羽的落羽2 小时前
【Linux系统】信号机制拆解,透过内核三张表深入本质
android·java·linux·服务器·c++·spring·机器学习
暴躁网友w2 小时前
UKF-IMM 与粒子滤波 IMM:计算效率 Matlab 仿真对比
开发语言·matlab
jxkejiiii2 小时前
电脑键盘震动反馈,开启与关闭方法及常见问题解答
java·安全·智能手机
小陳参上2 小时前
持久化数据库实现:确保数据持久性与可靠性
java·jvm·数据库
IT猿手2 小时前
基于控制障碍函数(CBF)的多无人机编队避障路径规划研究,MATLAB代码
开发语言·matlab·无人机·路径规划·动态路径规划