每日一学:设计模式之代理模式

代理模式是结构型设计模式 的一种,核心思想:给一个对象找一个 "代理人",让代理人代替原对象处理请求,原对象只做核心业务逻辑。

代理模式在Java的使用

声明式事务 @Transactional、AOP 切面增强 @Aspect等等

  • 日志记录
  • 权限校验
  • 耗时统计
  • 参数校验
  • 异常统一处理

代理对象负责重复的非业务操作,如记录前后日志、权限校验。原对象只需要关注核心业务。

一、核心概念

  1. 抽象主题(Subject):定义原对象和代理对象的公共接口(同时做的事情)
  2. 真实主题(RealSubject):真正做事的原对象(例如项目中的核心业务逻辑)
  3. 代理(Proxy):代替原对象处理请求,可附加额外功能(给业务加的日志等功能)
  4. 客户端(Client):使用代理对象,不直接接触原对象(调用代理对象,先执行完代理对象的操作再执行核心业务逻辑)

核心特点

  • 不修改原对象代码:通过代理扩展功能(开闭原则)
  • 职责分离:原对象只做核心业务,代理做日志、权限、缓存等附加操作
  • 保护 / 控制访问:代理可以拦截、过滤对原对象的请求

二、两种代理实现方式

1. 静态代理(手动写代理类)

适合代理对象固定的场景,代码写死,编译时就确定代理关系。

代码示例

java 复制代码
/**
 * @author T
 * @version 1.0   2026-04-19
 */
public interface UserService {
    void deleteUser(Long userId);
}


public class UserServiceImpl implements UserService{
    @Override
    public void deleteUser(Long userId) {
        // 这里我们来写核心的业务逻辑
        System.out.println("删除用户id为"+userId+"的用户!");
    }
}

public class ProxyServiceImpl implements UserService{

    private UserService userService;
    public ProxyServiceImpl(UserService userService){
        this.userService = userService;
    }

    @Override
    public void deleteUser(Long userId) {
        System.out.println("执行删除前的日志记录、权限校验");
        userService.deleteUser(userId);
        System.out.println("执行删除成功后的操作");
    }
}

public class Client {

    public static void main(String[] args) {
        UserService proxy = new ProxyServiceImpl(new UserServiceImpl());
        proxy.deleteUser(1L);
    }
}

输出:

2. 动态代理(自动生成代理类)

不用手动写代理类,运行时自动生成代理对象,适合批量代理多个类。Java 中常用两种:

  • JDK 动态代理:JDK 动态代理通过生成"实现接口的代理类",因此必须依赖接口,否则无法代理
  • CGLIB 动态代理:基于类(无需接口),通过继承 + 方法重写 + 方法拦截

JDK 动态代理示例

java 复制代码
public interface UserService {
    void deleteUser(Long userId);
}

public class UserServiceImpl implements UserService {
    @Override
    public void deleteUser(Long userId) {
        // 核心业务逻辑
        System.out.println("删除用户id为"+userId+"的用户!");
    }
}


public class DynamicProxyFactory {

    // 传入目标对象,返回 动态代理对象
    public static Object getProxy(Object target) {
        // 方法拦截处理器
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  // 类加载器
                target.getClass().getInterfaces(),   // 目标对象实现的接口
                (proxy, method, args) -> {
                    // ===================== 前置增强 =====================
                    System.out.println("执行删除前的日志记录、权限校验");

                    // ===================== 执行目标方法 =====================
                    Object result = method.invoke(target, args);

                    // ===================== 后置增强 =====================
                    System.out.println("执行删除成功后的操作");

                    return result;
                }
        );
    }
}

输出:

CGLIB动态代理示例

java 复制代码
public class UserService {
    public void deleteUser(Long userId) {
        System.out.println("删除用户id为"+userId+"的用户!");
    }
}


public class CglibProxyFactory {

    public static UserService getProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        // 继承目标类,生成子类代理
        enhancer.setSuperclass(clazz);
        // 设置回调拦截器
        enhancer.setCallback(new UserMethodInterceptor());

        return (UserService) enhancer.create();
    }
}

public class UserMethodInterceptor implements MethodInterceptor {

    /**
     * 拦截所有目标方法执行
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

        // 前置增强
        System.out.println("CGLIB前置:日志记录、权限校验");

        // 执行目标原有方法
        Object result = proxy.invokeSuper(obj, args);

        // 后置增强
        System.out.println("CGLIB后置:删除操作完毕");

        return result;
    }
}

public class Client {
    public static void main(String[] args) {
        // 直接获取代理对象,无需接口
        UserService proxy = CglibProxyFactory.getProxy(UserService.class);
        proxy.deleteUser(1L);
    }
}

结果

三、代理模式的典型应用场景

  1. 远程代理:代理远程对象(比如 RPC 框架、微服务调用)
  2. 虚拟代理:延迟创建耗资源的对象(比如:网页先显示图片占位符,加载完再显示真实图片)
  3. 安全代理:控制访问权限(比如:接口权限校验)
  4. 日志 / 监控代理:统一记录日志、统计接口耗时
  5. 缓存代理:给方法加缓存,避免重复执行
  6. Spring AOP:底层就是动态代理,实现切面编程(日志、事务、权限)

四、静态代理 vs 动态代理

特点 静态代理 动态代理
实现方式 手动编写代理类 运行时自动生成代理对象
灵活性 低(一个类一个代理) 高(一个代理类代理多个类)
效率 编译期确定,性能高 运行时生成,略低
适用场景 代理对象少且固定 批量代理、框架开发

五、CGlib和JDK动态代理

  • JDK 动态代理

    • 基于接口实现
    • 底层反射
    • 目标类可以不用继承
    • JDK 动态代理通过生成"实现接口的代理类",因此必须依赖接口,否则无法代理
  • CGLIB 动态代理

    • 基于继承子类实现
    • 无需接口
    • 不能代理 final 类、final 方法
    • Spring 没有接口时自动用 CGLIB

六、总结

  1. 代理模式 = 代理人 + 真实对象,代理帮原对象处理杂事,原对象专注核心业务
  2. 核心价值:不修改原代码,扩展功能(日志、权限、缓存等)
  3. 两种实现:静态代理(简单)、动态代理(灵活,Spring 底层核心)
相关推荐
SL_staff2 分钟前
目标健康度自检清单:开发者视角下的目标-计划-任务链路断点诊断
java·开源·github
for_ever_love__6 分钟前
MySQL 高频面试题 30 问:索引、事务、锁、日志、主从与分库分表(MySQL 8.0 版)
java·数据库·spring boot·mysql·spring cloud·面试·总结
神仙别闹7 分钟前
基于C++实现的贪吃蛇游戏平台
java·c++·游戏
布吉岛的石头17 分钟前
Java 程序员第 49 阶段11:Java 接 BERT:用 ONNX Runtime 做句向量推理
java·人工智能·python·深度学习·bert·transformer
染指111024 分钟前
140.Agent-多Agent框架-Agent执行Skills流程
数据库·人工智能·设计模式·langchain·agents
niucloud-admin25 分钟前
JAVA V6 多商户商城 开发文档——JAVA服务端
java·开发语言
alexlifexyz40 分钟前
写了个 Claude Code 插件:AI 写的 Java 违反阿里规约,就写不进文件
java
威哥爱编程1 小时前
【AI全栈12-01】Spring Boot 为何是 AI 全栈后端优选
java·spring boot·后端
威哥爱编程1 小时前
【AI全栈12-03】Spring Boot 用多模型路由把智能客服成本降下来
java·spring boot·后端
威哥爱编程1 小时前
【AI全栈12-02】Spring Boot 跑通第一个 AI 对话接口:HR 政策问答机器人实战
java·spring boot·后端