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

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

代理模式在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 底层核心)
相关推荐
javaDocker2 小时前
沉浸式AI编程:IDEA + Claude Code 的终极方案
java·intellij-idea·ai编程
何妨呀~2 小时前
K8s+Docker部署实战
java·linux·kubernetes
手握风云-2 小时前
基于倒排索引的 Java 文档搜索引擎(一)
java·搜索引擎
无籽西瓜a2 小时前
【西瓜带你学设计模式 | 第十九期 - 状态模式】状态模式 —— 状态流转与行为切换实现、优缺点与适用场景
java·后端·设计模式·状态模式·软件工程
Han.miracle2 小时前
微服务注册中心实操:Eureka+Zookeeper对比+CAP定理详解
java·spring boot·spring
llm大模型算法工程师weng2 小时前
Java面试核心突破:面向对象与设计模式
java·设计模式·面试
weixin_520649872 小时前
xml json ini 文件语法
xml·java·json
user_admin_god2 小时前
AI编码OpenCode入门到入神
java·人工智能