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

5 阅读4分钟

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


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

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

角色

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

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


二、静态代理

实现方式

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

示例

java

// 抽象主题
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.Proxy 和 InvocationHandler
  • 特点:代理类在运行时生成,实现目标接口,所有方法调用都转发给 InvocationHandler 的 invoke 方法。

示例

java

// 处理器
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.Enhancer 和 MethodInterceptor

示例(Spring 的 CGLIB 实现):

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)的基础。理解代理模式,不仅有助于掌握框架原理,还能在实际开发中灵活运用,写出更简洁、可维护的代码。