java开发模式之静态代理、动态代理、CGLIB代理

代理模式是 Java 中一种常见的设计模式,它通过引入一个代理对象来控制对目标对象的访问,从而在不修改目标对象代码的前提下,增强其功能或控制其访问方式。


一、代理模式的定义与结构

定义:为其他对象提供一种代理以控制对这个对象的访问。

角色

  • 抽象主题(Subject) :定义真实主题和代理的共同接口。
  • 真实主题(RealSubject) :真正执行业务逻辑的对象。
  • 代理(Proxy) :持有真实主题的引用,在调用真实主题前后执行额外操作(如权限校验、日志、延迟加载等)。

代理模式的核心是间接访问,客户端不直接操作真实对象,而是通过代理。


二、静态代理

实现方式

在编译期就确定代理类,代理类与真实类实现相同接口。

示例

java

typescript 复制代码
// 抽象主题
interface UserService {
    void saveUser(String name);
}

// 真实主题
class UserServiceImpl implements UserService {
    @Override
    public void saveUser(String name) {
        System.out.println("保存用户:" + name);
    }
}

// 静态代理
class UserServiceProxy implements UserService {
    private UserService target;
    
    public UserServiceProxy(UserService target) {
        this.target = target;
    }
    
    @Override
    public void saveUser(String name) {
        System.out.println("开始事务");
        target.saveUser(name);
        System.out.println("提交事务");
    }
}

// 使用
UserService real = new UserServiceImpl();
UserService proxy = new UserServiceProxy(real);
proxy.saveUser("Alice");

优缺点

  • 优点:结构简单,易于理解。
  • 缺点:每个代理类只能为一个接口服务,如果接口很多,会产生大量代理类;且需要硬编码代理逻辑。

三、动态代理

动态代理在运行时动态生成代理类,无需手动编写每个代理类。Java 中主要有两种实现方式。

1. JDK 动态代理(基于接口)

  • 要求:目标对象必须实现至少一个接口。
  • 核心java.lang.reflect.ProxyInvocationHandler
  • 特点 :代理类在运行时生成,实现目标接口,所有方法调用都转发给 InvocationHandlerinvoke 方法。

示例

java

typescript 复制代码
// 处理器
class LogHandler implements InvocationHandler {
    private Object target;
    public LogHandler(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("调用后:" + method.getName());
        return result;
    }
}

// 生成代理
UserService real = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    real.getClass().getClassLoader(),
    real.getClass().getInterfaces(),
    new LogHandler(real)
);
proxy.saveUser("Alice");

2. CGLIB 动态代理(基于子类)

  • 要求 :目标类不能是 final,方法不能是 final/static
  • 原理:通过字节码技术生成目标类的子类作为代理,重写父类方法实现拦截。
  • 核心net.sf.cglib.proxy.EnhancerMethodInterceptor

示例(Spring 的 CGLIB 实现):

java

java 复制代码
class UserDao {
    public void insert() {
        System.out.println("插入数据");
    }
}

class DaoInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("前置处理");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("后置处理");
        return result;
    }
}

// 生成代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserDao.class);
enhancer.setCallback(new DaoInterceptor());
UserDao proxy = (UserDao) enhancer.create();
proxy.insert();

动态代理的优缺点

  • 优点:一个处理器可以为多个目标对象服务,代理类无需手动编写,灵活性强。
  • 缺点 :JDK 动态代理要求必须有接口;CGLIB 无法代理 final 类和方法。

四、代理模式的应用场景

场景 说明
AOP(面向切面编程) Spring AOP 使用动态代理实现方法拦截,如事务、日志、性能监控。
远程调用(RPC) 如 Dubbo、Feign,客户端通过代理对象隐藏网络通信细节。
延迟加载 Hibernate 等 ORM 框架通过代理实现懒加载,访问属性时才触发数据库查询。
权限控制 代理类在调用前检查用户是否有权限执行该方法。
缓存 代理类在调用真实方法前检查缓存,避免重复计算或访问数据库。
日志与监控 统一记录方法调用参数、耗时等信息。

五、静态代理 vs 动态代理

对比项 静态代理 动态代理
代理类生成时间 编译期 运行期
代码量 每个目标类需要一个代理类 一个处理器可代理多个类
灵活性 低,接口变更需修改代理类 高,无需手动维护代理类
性能 直接调用,略快 反射或字节码生成,略慢(可接受)
典型使用 简单场景 Spring AOP、RPC 框架

六、总结

代理模式是 Java 中实现功能增强和访问控制的重要手段。静态代理 简单直观,但扩展性差;动态代理利用运行时代码生成,成为主流框架(如 Spring)的基础。理解代理模式,不仅有助于掌握框架原理,还能在实际开发中灵活运用,写出更简洁、可维护的代码。

相关推荐
冬夜戏雪1 小时前
实习面经摘录(八)
java
拾年2751 小时前
别再让 NullPointerException 搞崩你的代码了!Optional + Stream 组合拳详解
java
weixin_404157681 小时前
Java高级面试与工程实践问题集(一)
java·开发语言·面试
cyforkk1 小时前
Spring AOP 进阶:揭秘 @annotation 参数绑定的底层逻辑
java·数据库·spring
清风徐来QCQ1 小时前
Java2(valueOf,Character,StringBuilder,设计模式)
java·开发语言
台XX1 小时前
Java容器常用方法
java·开发语言
tonyhi61 小时前
Ubuntu DeepSeek R1本地化部署 Ollama+Docker+OpenWebUI
java·ubuntu·docker
庞轩px2 小时前
面经分享1
java·笔记·面试
Yang-Never2 小时前
OpenGL ES ->YUV图像基础知识
android·java·开发语言·kotlin·android studio