代理模式是 Java 设计模式中的结构型模式之一,其核心思想是为目标对象提供一个代理对象,通过代理对象控制对目标对象的访问。代理模式可以在不修改目标对象代码的前提下,增强目标对象的功能(如权限控制、日志记录、性能监控等),同时隐藏目标对象的具体实现细节。

一、代理模式的核心角色

代理模式包含三个核心角色:

  1. 抽象主题(Subject):定义目标对象和代理对象的共同接口,确保代理对象与目标对象具有一致的行为,客户端可通过该接口统一访问。
  2. 目标对象(RealSubject):实现抽象主题接口,是代理对象所代表的真实业务逻辑执行者。
  3. 代理对象(Proxy):同样实现抽象主题接口,内部持有目标对象的引用,在调用目标对象方法的前后可以添加额外逻辑。

二、代理模式的分类与实现

根据代理对象的创建时机和实现方式,Java 中的代理模式主要分为静态代理动态代理(JDK 动态代理、CGLIB 动态代理)。

1. 静态代理

静态代理是在编译期就确定代理类与目标类的关系,代理类由开发者手动编写,编译后生成.class 文件。

实现步骤:
  • 定义抽象主题接口;
  • 实现目标类(RealSubject);
  • 编写代理类(Proxy),持有目标对象引用,并在方法中增强逻辑。
示例代码:
// 1. 抽象主题接口
public interface UserService {
    void addUser(String username);
}

// 2. 目标对象:实现抽象主题
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("添加用户:" + username);
    }
}

// 3. 代理对象:增强目标对象功能
public class UserServiceProxy implements UserService {
    // 持有目标对象引用
    private UserService target;

    public UserServiceProxy(UserService target) {
        this.target = target;
    }

    @Override
    public void addUser(String username) {
        // 前置增强:日志记录
        System.out.println("【日志】开始添加用户,用户名:" + username);
        // 调用目标对象方法
        target.addUser(username);
        // 后置增强:结果校验
        System.out.println("【日志】用户添加完成");
    }
}

// 客户端调用
public class Client {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = new UserServiceProxy(target);
        proxy.addUser("张三");
    }
}
静态代理的优缺点:
  • 优点:实现简单,逻辑清晰,不侵入目标对象代码;
  • 缺点:代理类与目标类一一对应,若接口方法增多,代理类需同步修改,维护成本高;无法处理大量目标类的代理需求。

2. 动态代理

动态代理是在运行期动态生成代理类,无需手动编写代理类代码,能灵活处理多个目标类的代理需求。Java 中常见的动态代理方式有JDK 动态代理CGLIB 动态代理

(1)JDK 动态代理

JDK 动态代理基于 Java 反射机制实现,要求目标类必须实现接口(因为代理类会动态实现目标类的接口)。核心类为java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

实现步骤:
  • 定义 InvocationHandler 实现类,重写invoke方法(包含增强逻辑和目标方法调用);
  • 通过Proxy.newProxyInstance()生成代理对象。
示例代码:
// 1. 抽象主题接口(复用上述UserService)
// 2. 目标对象(复用上述UserServiceImpl)

// 3. 自定义InvocationHandler
public class MyInvocationHandler implements InvocationHandler {
    // 目标对象(通用类型,适配不同接口)
    private Object target;

    public MyInvocationHandler(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() + "方法执行耗时:10ms");
        return result;
    }
}

// 客户端调用
public class Client {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        // 生成动态代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),  // 目标类类加载器
            target.getClass().getInterfaces(),   // 目标类实现的接口
            new MyInvocationHandler(target)      // 自定义InvocationHandler
        );
        proxy.addUser("李四");
    }
}
JDK 动态代理的特点:
  • 基于接口实现,代理类与目标类解耦;
  • 无需手动编写代理类,运行期动态生成,灵活性高;
  • 缺点:目标类必须实现接口,否则无法使用。
(2)CGLIB 动态代理

CGLIB(Code Generation Library)是基于 ASM 字节码生成框架实现的动态代理,无需目标类实现接口,通过继承目标类生成子类作为代理类。

实现步骤:
  • 引入 CGLIB 依赖(Spring 等框架已内置);
  • 定义 MethodInterceptor 实现类,重写intercept方法;
  • 通过 Enhancer 类生成代理对象。
示例代码:
// 1. 目标类(无需实现接口)
public class OrderService {
    public void createOrder(String orderNo) {
        System.out.println("创建订单:" + orderNo);
    }
}

// 2. 自定义MethodInterceptor
public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 前置增强:日志记录
        System.out.println("【CGLIB代理】调用方法:" + method.getName());
        // 调用目标对象方法
        Object result = proxy.invokeSuper(obj, args);
        // 后置增强:结果处理
        System.out.println("【CGLIB代理】方法执行完成");
        return result;
    }
}

// 客户端调用
public class Client {
    public static void main(String[] args) {
        // 创建Enhancer对象,用于生成代理类
        Enhancer enhancer = new Enhancer();
        // 设置父类(目标类)
        enhancer.setSuperclass(OrderService.class);
        // 设置回调(增强逻辑)
        enhancer.setCallback(new MyMethodInterceptor());
        // 生成代理对象
        OrderService proxy = (OrderService) enhancer.create();
        proxy.createOrder("ORDER_123456");
    }
}
CGLIB 动态代理的特点:
  • 无需目标类实现接口,适用性更广;
  • 通过继承实现,目标类不能是 final(否则无法生成子类);
  • 性能略优于 JDK 动态代理(JDK 8 后差距缩小)。

三、代理模式的应用场景

  1. Spring AOP:Spring AOP 底层结合 JDK 动态代理(目标类实现接口)和 CGLIB 动态代理(目标类无接口),实现横切逻辑(如事务管理、日志、缓存)的统一增强。
  2. 远程代理:如 RPC 框架(Dubbo)中,客户端通过代理对象调用远程服务器的方法,隐藏网络通信细节。
  3. 虚拟代理:延迟加载重量级对象,如 Hibernate 的懒加载(Lazy Loading),通过代理对象延迟初始化真实数据。
  4. 保护代理:控制对目标对象的访问权限,如权限校验框架中,通过代理拦截未授权的方法调用。

四、静态代理与动态代理的对比

特性静态代理JDK 动态代理CGLIB 动态代理
实现方式手动编写代理类反射 + 接口字节码生成 + 继承
目标类要求需实现接口(非必须)必须实现接口无需实现接口,不能是 final
灵活性低(需手动维护)高(运行期动态生成)高(运行期动态生成)
性能高(编译期确定)中(反射开销)高(字节码直接生成)

总结

代理模式通过引入代理对象实现对目标对象的间接访问,既保护了目标对象的封装性,又能灵活扩展功能。静态代理适用于简单场景,动态代理则在框架开发中发挥核心作用(如 Spring AOP)。实际开发中,需根据目标类是否实现接口、性能要求等选择合适的代理方式。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐