Java中的静态代理与动态代理详解
字数 1677 2025-11-11 06:58:35

Java中的静态代理与动态代理详解

1. 代理模式的基本概念

代理模式是一种设计模式,通过一个代理对象控制对原始对象的访问。代理可以在不修改原始对象的基础上,增强其功能(如添加日志、权限校验等)。代理分为两类:

  • 静态代理:在编译期确定代理关系,代理类需手动编码。
  • 动态代理:在运行期动态生成代理类,无需预先编写代理类代码。

2. 静态代理的实现与局限性

2.1 静态代理示例

假设有一个接口 UserService 和其实现类:

public interface UserService {
    void saveUser(String user);
}

public class UserServiceImpl implements UserService {
    @Override
    public void saveUser(String user) {
        System.out.println("保存用户:" + user);
    }
}

静态代理类需要实现相同接口,并在内部调用原始对象的方法:

public class UserServiceProxy implements UserService {
    private UserService target; // 目标对象

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

    @Override
    public void saveUser(String user) {
        System.out.println("前置增强:权限校验");
        target.saveUser(user); // 调用目标方法
        System.out.println("后置增强:记录日志");
    }
}

2.2 静态代理的缺点

  • 代码冗余:每个目标类都需要对应一个代理类,难以维护。
  • 灵活性差:接口变更时,代理类需同步修改。

3. 动态代理的核心机制

动态代理通过反射机制在运行时动态生成代理类。Java提供了两种实现方式:

  1. JDK 动态代理:基于接口生成代理类。
  2. CGLIB 动态代理:通过继承目标类生成子类代理(无需接口)。

4. JDK 动态代理详解

4.1 核心类与接口

  • java.lang.reflect.Proxy:提供创建代理对象的静态方法(如 newProxyInstance)。
  • java.lang.reflect.InvocationHandler:代理类的调用处理器,需实现 invoke 方法。

4.2 实现步骤

步骤1:定义调用处理器(实现 InvocationHandler):

public class LogInvocationHandler implements InvocationHandler {
    private Object target; // 目标对象

    public LogInvocationHandler(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;
    }
}

步骤2:通过 Proxy 创建代理对象:

public class Main {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(), // 类加载器
            target.getClass().getInterfaces(),  // 目标类实现的接口
            new LogInvocationHandler(target)    // 调用处理器
        );
        proxy.saveUser("Alice"); // 调用代理方法
    }
}

4.3 JDK 动态代理的限制

  • 目标类必须实现接口,否则无法生成代理。

5. CGLIB 动态代理详解

5.1 基本原理

CGLIB(Code Generation Library)通过字节码技术生成目标类的子类,重写父类方法实现代理。

5.2 实现步骤

步骤1:添加 CGLIB 依赖(Maven):

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

步骤2:定义方法拦截器(实现 MethodInterceptor):

public class CglibProxyInterceptor 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;
    }
}

步骤3:通过 Enhancer 创建代理对象:

public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class); // 设置目标类为父类
        enhancer.setCallback(new CglibProxyInterceptor()); // 设置拦截器

        UserServiceImpl proxy = (UserServiceImpl) enhancer.create(); // 创建代理对象
        proxy.saveUser("Bob");
    }
}

5.3 CGLIB 的特点与限制

  • 优点:无需接口,可直接代理普通类。
  • 缺点
    • 无法代理 final 类或 final 方法(无法被重写)。
    • 生成代理类的过程比 JDK 动态代理稍慢。

6. 静态代理与动态代理的对比

特性 静态代理 JDK 动态代理 CGLIB 动态代理
实现方式 手动编写代理类 基于接口反射 基于子类继承
编译期依赖 需要 不需要 不需要
目标类要求 需实现接口 需实现接口 不能是 final 类或方法
性能 高(直接调用) 中等(反射调用) 较高(方法索引优化)

7. 动态代理的实际应用场景

  1. Spring AOP:基于 JDK 动态代理(接口)和 CGLIB(无接口)实现切面编程。
  2. RPC 框架:代理远程方法调用,封装网络通信细节。
  3. 日志记录、事务管理:无侵入式增强业务方法。

通过以上步骤,你可以深入理解 Java 中代理模式的实现原理及适用场景,为后续学习 Spring 等框架打下基础。

Java中的静态代理与动态代理详解 1. 代理模式的基本概念 代理模式 是一种设计模式,通过一个代理对象控制对原始对象的访问。代理可以在不修改原始对象的基础上,增强其功能(如添加日志、权限校验等)。代理分为两类: 静态代理 :在编译期确定代理关系,代理类需手动编码。 动态代理 :在运行期动态生成代理类,无需预先编写代理类代码。 2. 静态代理的实现与局限性 2.1 静态代理示例 假设有一个接口 UserService 和其实现类: 静态代理类 需要实现相同接口,并在内部调用原始对象的方法: 2.2 静态代理的缺点 代码冗余 :每个目标类都需要对应一个代理类,难以维护。 灵活性差 :接口变更时,代理类需同步修改。 3. 动态代理的核心机制 动态代理通过 反射机制 在运行时动态生成代理类。Java提供了两种实现方式: JDK 动态代理 :基于接口生成代理类。 CGLIB 动态代理 :通过继承目标类生成子类代理(无需接口)。 4. JDK 动态代理详解 4.1 核心类与接口 java.lang.reflect.Proxy :提供创建代理对象的静态方法(如 newProxyInstance )。 java.lang.reflect.InvocationHandler :代理类的调用处理器,需实现 invoke 方法。 4.2 实现步骤 步骤1 :定义调用处理器(实现 InvocationHandler ): 步骤2 :通过 Proxy 创建代理对象: 4.3 JDK 动态代理的限制 目标类 必须实现接口 ,否则无法生成代理。 5. CGLIB 动态代理详解 5.1 基本原理 CGLIB(Code Generation Library)通过 字节码技术 生成目标类的子类,重写父类方法实现代理。 5.2 实现步骤 步骤1 :添加 CGLIB 依赖(Maven): 步骤2 :定义方法拦截器(实现 MethodInterceptor ): 步骤3 :通过 Enhancer 创建代理对象: 5.3 CGLIB 的特点与限制 优点 :无需接口,可直接代理普通类。 缺点 : 无法代理 final 类或 final 方法(无法被重写)。 生成代理类的过程比 JDK 动态代理稍慢。 6. 静态代理与动态代理的对比 | 特性 | 静态代理 | JDK 动态代理 | CGLIB 动态代理 | |--------------|------------------------------|----------------------------|---------------------------| | 实现方式 | 手动编写代理类 | 基于接口反射 | 基于子类继承 | | 编译期依赖 | 需要 | 不需要 | 不需要 | | 目标类要求 | 需实现接口 | 需实现接口 | 不能是 final 类或方法 | | 性能 | 高(直接调用) | 中等(反射调用) | 较高(方法索引优化) | 7. 动态代理的实际应用场景 Spring AOP :基于 JDK 动态代理(接口)和 CGLIB(无接口)实现切面编程。 RPC 框架 :代理远程方法调用,封装网络通信细节。 日志记录、事务管理 :无侵入式增强业务方法。 通过以上步骤,你可以深入理解 Java 中代理模式的实现原理及适用场景,为后续学习 Spring 等框架打下基础。