Java 的静态代理和动态代理

文章目录

    • [一、Java 代理模式的核心价值](#一、Java 代理模式的核心价值)
    • 二、代理类型对比
    • 三、静态代理示例
      • [1. 基础结构](#1. 基础结构)
      • [2. 静态代理的优缺点](#2. 静态代理的优缺点)
    • [四、JDK 动态代理](#四、JDK 动态代理)
      • [1. 基础实现](#1. 基础实现)
      • [2. 通用动态代理工厂](#2. 通用动态代理工厂)
    • [五、CGLIB 动态代理(代理无接口的类)](#五、CGLIB 动态代理(代理无接口的类))
      • [1. 添加依赖](#1. 添加依赖)
      • [2. 基础实现](#2. 基础实现)
      • [3. CGLIB 原理与限制](#3. CGLIB 原理与限制)
    • 六、性能测试
    • 七、实际应用场景
      • [1. Spring AOP 的实现](#1. Spring AOP 的实现)
      • [2. MyBatis 的 Mapper 代理](#2. MyBatis 的 Mapper 代理)
      • [3. RPC 框架](#3. RPC 框架)
    • 总结

一、Java 代理模式的核心价值

代理类作为目标类与调用方的中间层,核心作用体现在 4 个方面:

  1. 无侵入式功能增强:为目标类添加通用横切逻辑(日志、监控、事务、权限校验),核心业务代码保持纯净;
  2. 解耦核心与非核心逻辑:将日志、权限等通用逻辑抽离到代理类,符合"单一职责原则";
  3. 控制目标类访问:限制对目标类的调用(如仅允许管理员调用),或封装远程调用、懒加载等复杂逻辑;
  4. 简化复杂对象访问:封装目标对象的初始化、资源释放等细节,调用方无需关注底层实现。

二、代理类型对比

  • 静态代理编译期手动编写 代理类,编译后生成独立的 .class 文件,与目标类的绑定关系在编译期确定。

  • 动态代理运行期通过反射/字节码生成 代理类(无物理 .class 文件),无需手动编写,可通用适配任意目标类。

特性 静态代理 动态代理(JDK) 动态代理(CGLIB)
是否需要接口 ✅ 需要 ✅ 需要 ❌ 不需要
性能 中(比JDK略慢)
灵活性
使用复杂度

关键点 :只有 CGLIB 可以代理没有接口的类,JDK 动态代理和静态代理都需要接口。

三、静态代理示例

1. 基础结构

java 复制代码
// 1. 接口
interface UserService {
    void addUser(String username);
    String getUser(int id);
}

// 2. 真实对象
class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username) {
        System.out.println("添加用户: " + username);
    }
    
    @Override
    public String getUser(int id) {
        return "用户" + id;
    }
}

// 3. 静态代理类
class UserServiceStaticProxy implements UserService {
    private UserService target;  // 持有真实对象
    
    public UserServiceStaticProxy(UserService target) {
        this.target = target;
    }
    
    @Override
    public void addUser(String username) {
        long start = System.currentTimeMillis();
        System.out.println("【代理】开始记录日志...");
        
        // 调用真实方法
        target.addUser(username);
        
        long end = System.currentTimeMillis();
        System.out.println("【代理】方法执行时间: " + (end - start) + "ms");
    }
    
    @Override
    public String getUser(int id) {
        System.out.println("【代理】权限验证...");
        
        if (id <= 0) {
            throw new IllegalArgumentException("ID必须大于0");
        }
        
        // 调用真实方法
        return target.getUser(id);
    }
}

// 4. 使用示例
public class StaticProxyDemo {
    public static void main(String[] args) {
        // 创建真实对象
        UserService realService = new UserServiceImpl();
        
        // 创建代理对象
        UserService proxy = new UserServiceStaticProxy(realService);
        
        // 通过代理调用
        proxy.addUser("张三");
        System.out.println(proxy.getUser(1));
    }
}

2. 静态代理的优缺点

java 复制代码
// 优点:直观、简单,编译器检查
// 缺点:每个代理类只能代理一个接口,代码冗余

// 如果有多个接口需要代理,需要写多个代理类
interface OrderService {
    void createOrder();
}

class OrderServiceImpl implements OrderService {
    public void createOrder() {
        System.out.println("创建订单");
    }
}

// 又需要写一个代理类!
class OrderServiceStaticProxy implements OrderService {
    private OrderService target;
    
    public OrderServiceStaticProxy(OrderService target) {
        this.target = target;
    }
    
    @Override
    public void createOrder() {
        // 重复的增强逻辑...
        System.out.println("【代理】记录日志");
        target.createOrder();
    }
}

四、JDK 动态代理

1. 基础实现

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 1. 增强逻辑处理器
class LoggingInvocationHandler implements InvocationHandler {
    private final Object target;  // 真实对象
    
    public LoggingInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 前置增强
        System.out.println("【动态代理】开始调用: " + method.getName());
        long start = System.currentTimeMillis();
        
        try {
            // 调用真实方法
            Object result = method.invoke(target, args);
            
            // 后置增强
            long end = System.currentTimeMillis();
            System.out.println("【动态代理】调用完成,耗时: " + (end - start) + "ms");
            return result;
            
        } catch (Exception e) {
            // 异常增强
            System.out.println("【动态代理】调用异常: " + e.getMessage());
            throw e;
        }
    }
}

// 2. 权限验证处理器
class AuthInvocationHandler implements InvocationHandler {
    private final Object target;
    private final String currentUser;
    
    public AuthInvocationHandler(Object target, String currentUser) {
        this.target = target;
        this.currentUser = currentUser;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 权限校验
        if ("admin".equals(currentUser)) {
            return method.invoke(target, args);
        } else {
            throw new SecurityException("用户 " + currentUser + " 无权限执行此操作");
        }
    }
}

// 3. 使用示例
public class JdkDynamicProxyDemo {
    public static void main(String[] args) {
        // 创建真实对象
        UserService realService = new UserServiceImpl();
        
        // 创建 InvocationHandler
        InvocationHandler handler = new LoggingInvocationHandler(realService);
        
        // 创建代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
            realService.getClass().getClassLoader(),  // 类加载器
            realService.getClass().getInterfaces(),    // 实现的接口
            handler                                    // 增强逻辑处理器
        );
        
        // 测试
        proxy.addUser("李四");
        System.out.println(proxy.getUser(2));
        
        // 链式增强:先权限验证,再记录日志
        System.out.println("\n=== 链式增强 ===");
        UserService authProxy = (UserService) Proxy.newProxyInstance(
            realService.getClass().getClassLoader(),
            realService.getClass().getInterfaces(),
            new AuthInvocationHandler(realService, "user")  // 非admin用户
        );
        
        try {
            authProxy.addUser("王五");  // 会抛出权限异常
        } catch (Exception e) {
            System.out.println("权限验证失败: " + e.getMessage());
        }
    }
}

2. 通用动态代理工厂

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

// 增强处理器接口
interface Interceptor {
    boolean before(Object target, Method method, Object[] args);
    void after(Object target, Method method, Object[] args, Object result);
    void afterThrowing(Object target, Method method, Object[] args, Throwable throwable);
}

// 日志拦截器
class LogInterceptor implements Interceptor {
    @Override
    public boolean before(Object target, Method method, Object[] args) {
        System.out.println("[日志] 开始执行: " + method.getName());
        return true;  // 继续执行
    }
    
    @Override
    public void after(Object target, Method method, Object[] args, Object result) {
        System.out.println("[日志] 执行完成: " + method.getName());
    }
    
    @Override
    public void afterThrowing(Object target, Method method, Object[] args, Throwable throwable) {
        System.out.println("[日志] 执行异常: " + method.getName() + ", 异常: " + throwable.getMessage());
    }
}

// 监控拦截器
class MonitorInterceptor implements Interceptor {
    private ThreadLocal<Long> startTime = new ThreadLocal<>();
    
    @Override
    public boolean before(Object target, Method method, Object[] args) {
        startTime.set(System.currentTimeMillis());
        return true;
    }
    
    @Override
    public void after(Object target, Method method, Object[] args, Object result) {
        long end = System.currentTimeMillis();
        System.out.println("[监控] " + method.getName() + " 耗时: " + (end - startTime.get()) + "ms");
        startTime.remove();
    }
    
    @Override
    public void afterThrowing(Object target, Method method, Object[] args, Throwable throwable) {
        startTime.remove();
    }
}

// 通用代理工厂
class ProxyFactory {
    public static <T> T createProxy(T target, Interceptor... interceptors) {
        List<Interceptor> interceptorList = List.of(interceptors);
        
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 执行前置增强
                    for (Interceptor interceptor : interceptorList) {
                        if (!interceptor.before(target, method, args)) {
                            return null;  // 被拦截
                        }
                    }
                    
                    try {
                        // 执行目标方法
                        Object result = method.invoke(target, args);
                        
                        // 执行后置增强
                        for (Interceptor interceptor : interceptorList) {
                            interceptor.after(target, method, args, result);
                        }
                        
                        return result;
                    } catch (Throwable t) {
                        // 执行异常增强
                        for (Interceptor interceptor : interceptorList) {
                            interceptor.afterThrowing(target, method, args, t);
                        }
                        throw t;
                    }
                }
            }
        );
    }
}

// 使用通用工厂
public class GenericProxyDemo {
    public static void main(String[] args) {
        UserService realService = new UserServiceImpl();
        
        // 创建带多个增强的代理
        UserService enhancedProxy = ProxyFactory.createProxy(
            realService,
            new LogInterceptor(),
            new MonitorInterceptor()
        );
        
        enhancedProxy.addUser("赵六");
        System.out.println(enhancedProxy.getUser(3));
    }
}

五、CGLIB 动态代理(代理无接口的类)

1. 添加依赖

xml 复制代码
<!-- Maven 依赖 -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

2. 基础实现

java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 1. 没有接口的类
class UserDao {
    public void save(String user) {
        System.out.println("保存用户: " + user);
    }
    
    public String findById(int id) {
        return "用户" + id;
    }
    
    public final void finalMethod() {  // final 方法不能被代理
        System.out.println("这是final方法");
    }
}

// 2. 方法拦截器
class CglibMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) 
            throws Throwable {
        
        System.out.println("【CGLIB代理】前置增强: " + method.getName());
        
        long start = System.currentTimeMillis();
        
        try {
            // 调用父类(真实对象)的方法
            Object result = proxy.invokeSuper(obj, args);
            
            long end = System.currentTimeMillis();
            System.out.println("【CGLIB代理】后置增强,耗时: " + (end - start) + "ms");
            
            return result;
        } catch (Exception e) {
            System.out.println("【CGLIB代理】异常增强: " + e.getMessage());
            throw e;
        }
    }
}

// 3. 回调过滤器(选择性地增强某些方法)
class CglibCallbackFilter implements net.sf.cglib.proxy.CallbackFilter {
    @Override
    public int accept(Method method) {
        if ("save".equals(method.getName())) {
            return 0;  // 使用第一个回调(增强)
        } else if ("findById".equals(method.getName())) {
            return 1;  // 使用第二个回调(不增强)
        } else {
            return 2;  // 使用第三个回调(固定返回值)
        }
    }
}

// 4. 固定值回调
class FixedValueCallback implements net.sf.cglib.proxy.FixedValue {
    @Override
    public Object loadObject() throws Exception {
        return "固定返回值";
    }
}

// 5. 使用示例
public class CglibProxyDemo {
    public static void main(String[] args) {
        // 基本使用
        System.out.println("=== 基本CGLIB代理 ===");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserDao.class);  // 设置父类
        enhancer.setCallback(new CglibMethodInterceptor());
        
        UserDao proxy = (UserDao) enhancer.create();
        proxy.save("CGLIB用户");
        System.out.println(proxy.findById(1));
        
        // 使用回调过滤器和多个回调
        System.out.println("\n=== 多回调CGLIB代理 ===");
        Enhancer enhancer2 = new Enhancer();
        enhancer2.setSuperclass(UserDao.class);
        
        // 设置多个回调
        enhancer2.setCallbacks(new net.sf.cglib.proxy.Callback[] {
            new CglibMethodInterceptor(),  // 增强回调
            net.sf.cglib.proxy.NoOp.INSTANCE,  // 不增强回调
            new FixedValueCallback()  // 固定返回值回调
        });
        
        enhancer2.setCallbackFilter(new CglibCallbackFilter());
        
        UserDao filteredProxy = (UserDao) enhancer2.create();
        filteredProxy.save("过滤用户");  // 会被增强
        System.out.println("findById: " + filteredProxy.findById(2));  // 不会被增强
    }
    
    // 6. 测试final方法
    public static void testFinalMethod() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserDao.class);
        enhancer.setCallback(new CglibMethodInterceptor());
        
        UserDao proxy = (UserDao) enhancer.create();
        proxy.finalMethod();  // 会调用原方法,不会被代理增强
    }
}

3. CGLIB 原理与限制

java 复制代码
// CGLIB 通过继承被代理类创建子类来实现代理
// 因此有以下限制:

class CglibLimitations {
    // 1. 无法代理 final 类
    final class FinalClass {  // 这个类不能被CGLIB代理
        public void method() {
            System.out.println("final class");
        }
    }
    
    // 2. 无法代理 final 方法
    class NormalClass {
        public final void finalMethod() {  // 这个方法不会被代理增强
            System.out.println("final method");
        }
        
        public void normalMethod() {  // 这个方法可以被代理
            System.out.println("normal method");
        }
    }
    
    // 3. 无法代理 private 方法
    class PrivateMethodClass {
        private void privateMethod() {  // 这个方法不会被代理
            System.out.println("private method");
        }
    }
    
    // 4. 必须有无参构造器
    class NoDefaultConstructor {
        public NoDefaultConstructor(String arg) {  // 缺少无参构造器
            // CGLIB 需要调用父类的无参构造器
        }
    }
}

六、性能测试

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class PerformanceComparison {
    interface Service {
        String process(String input);
    }
    
    static class ServiceImpl implements Service {
        @Override
        public String process(String input) {
            return input.toUpperCase();
        }
    }
    
    static class ServiceWithoutInterface {
        public String process(String input) {
            return input.toUpperCase();
        }
    }
    
    public static void main(String[] args) {
        int iterations = 1_000_000;
        
        // 1. 直接调用
        Service direct = new ServiceImpl();
        long start = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            direct.process("test");
        }
        long directTime = System.currentTimeMillis() - start;
        
        // 2. 静态代理
        Service staticProxy = new Service() {
            private Service target = new ServiceImpl();
            @Override
            public String process(String input) {
                return target.process(input);
            }
        };
        
        start = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            staticProxy.process("test");
        }
        long staticTime = System.currentTimeMillis() - start;
        
        // 3. JDK动态代理
        Service jdkProxy = (Service) Proxy.newProxyInstance(
            ServiceImpl.class.getClassLoader(),
            new Class[]{Service.class},
            new InvocationHandler() {
                private Service target = new ServiceImpl();
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    return method.invoke(target, args);
                }
            }
        );
        
        start = System.currentTimeMillis();
        for (int i = 0; i < iterations; i++) {
            jdkProxy.process("test");
        }
        long jdkTime = System.currentTimeMillis() - start;
        
        System.out.println("性能对比 (迭代" + iterations + "次):");
        System.out.println("直接调用: " + directTime + "ms");
        System.out.println("静态代理: " + staticTime + "ms");
        System.out.println("JDK动态代理: " + jdkTime + "ms");
    }
}

七、实际应用场景

1. Spring AOP 的实现

java 复制代码
// Spring 中的代理使用
@Component
class BusinessService {
    
    @Transactional
    public void businessMethod() {
        // 业务逻辑
    }
    
    @LogExecutionTime
    public void anotherMethod() {
        // 另一个业务逻辑
    }
}

// Spring 会根据配置选择代理方式:
// 1. 如果有接口 → 使用 JDK 动态代理
// 2. 如果没有接口 → 使用 CGLIB
// 3. 可以强制指定使用 CGLIB: @EnableAspectJAutoProxy(proxyTargetClass = true)

2. MyBatis 的 Mapper 代理

java 复制代码
// MyBatis 为每个 Mapper 接口创建代理
public class MapperProxy<T> implements InvocationHandler {
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 如果是 Object 方法,直接调用
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        
        // 2. 获取 SQL 语句
        String sql = getMappedStatement(method);
        
        // 3. 执行 SQL
        return sqlSession.selectOne(sql, args);
    }
}

3. RPC 框架

java 复制代码
// RPC 客户端代理
public class RpcClientProxy implements InvocationHandler {
    private String host;
    private int port;
    
    public RpcClientProxy(String host, int port) {
        this.host = host;
        this.port = port;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 序列化请求
        RpcRequest request = new RpcRequest();
        request.setClassName(method.getDeclaringClass().getName());
        request.setMethodName(method.getName());
        request.setParameters(args);
        
        // 2. 网络传输
        Socket socket = new Socket(host, port);
        ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
        oos.writeObject(request);
        
        // 3. 接收响应
        ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
        RpcResponse response = (RpcResponse) ois.readObject();
        
        return response.getResult();
    }
}

总结

  1. 为什么用代理:实现非侵入式增强,符合开闭原则
  2. 静态代理:需要接口,代码冗余,但性能好
  3. JDK动态代理:需要接口,运行时生成代理类
  4. CGLIB代理:不需要接口,通过继承实现,但不能代理final类/方法
  5. 选择建议
    • 有接口且性能要求高 → JDK动态代理
    • 无接口 → CGLIB
    • 简单场景、代理类少 → 静态代理
    • 复杂场景、需要灵活配置 → 动态代理

代理模式是 Spring、MyBatis 等主流框架的基石,理解代理模式对深入理解 Java 生态至关重要。

相关推荐
222you2 小时前
Java的Stream流
java·开发语言
kevinzeng2 小时前
Redis的IO多路复用
java·redis
2501_916766542 小时前
【SpringMVC】异常处理和拦截器
java·spring
不惑_2 小时前
在 Docker 中运行 Java JAR 包实战教程
java·docker·jar
一勺菠萝丶2 小时前
解决Java中IP地址访问HTTPS接口的SSL证书验证问题
java·tcp/ip·https
墨着染霜华2 小时前
IntelliJ IDEA 设置导出与导入完整指南(备份 / 迁移 / 团队共享)
java·ide·intellij-idea
浮游本尊2 小时前
Java学习第32天 - 性能优化与架构设计
java
五阿哥永琪2 小时前
Nacos注册/配置中心
java·开发语言