知识模块
☕ Java 知识模块
十一、设计模式
代理模式

代理模式

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

一、核心概念

1. 结构图

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Client    │────→│   Subject   │◄────│ RealSubject │
│   (客户端)   │     │  (抽象主题)  │     │  (真实主题)  │
└─────────────┘     └──────┬──────┘     └─────────────┘

                          │ 实现
                   ┌──────┴──────┐
                   │    Proxy    │
                   │   (代理)     │
                   │ ┌─────────┐ │
                   │ │realSubject│ │
                   │ └─────────┘ │
                   └─────────────┘

2. 代理类型

类型说明应用场景
远程代理代表远程对象RPC 调用
虚拟代理延迟创建开销大的对象图片懒加载
保护代理控制访问权限权限控制
智能引用增加额外功能引用计数、日志

二、静态代理

1. 实现

// 抽象主题
public interface Subject {
    void request();
}
 
// 真实主题
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实对象处理请求");
    }
}
 
// 代理类
public class Proxy implements Subject {
    private RealSubject realSubject;
    
    public Proxy() {
        this.realSubject = new RealSubject();
    }
    
    @Override
    public void request() {
        // 前置处理
        System.out.println("代理前置处理");
        
        // 调用真实对象
        realSubject.request();
        
        // 后置处理
        System.out.println("代理后置处理");
    }
}
 
// 使用
Subject subject = new Proxy();
subject.request();

2. 优缺点

优点

  • 代码直观易懂
  • 不需要反射

缺点

  • 每个真实对象都需要一个代理类
  • 代理类代码重复
  • 违反开闭原则

三、JDK 动态代理

1. 实现

// 接口
public interface UserService {
    void addUser(String name);
    void deleteUser(String name);
}
 
// 实现类
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("添加用户: " + name);
    }
    
    @Override
    public void deleteUser(String name) {
        System.out.println("删除用户: " + name);
    }
}
 
// 动态代理处理器
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("[日志] 方法执行完成");
        
        return result;
    }
}
 
// 使用
UserService userService = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    userService.getClass().getClassLoader(),
    userService.getClass().getInterfaces(),
    new LogInvocationHandler(userService)
);
 
proxy.addUser("张三");
proxy.deleteUser("李四");

2. 核心类

// 创建代理对象
Proxy.newProxyInstance(
    ClassLoader loader,    // 类加载器
    Class<?>[] interfaces, // 实现的接口
    InvocationHandler h    // 调用处理器
);
 
// InvocationHandler 接口
public interface InvocationHandler {
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

3. JDK 动态代理原理

JDK 动态代理生成的代理类:
1. 实现 target 的所有接口
2. 持有 InvocationHandler 引用
3. 方法调用转发给 InvocationHandler.invoke()
4. invoke() 中调用 target 的实际方法

四、CGLIB 动态代理

1. 实现

// 目标类(无需实现接口)
public class UserService {
    public void addUser(String name) {
        System.out.println("添加用户: " + name);
    }
    
    public void deleteUser(String name) {
        System.out.println("删除用户: " + name);
    }
}
 
// CGLIB 方法拦截器
public class CglibMethodInterceptor 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;
    }
}
 
// 使用
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new CglibMethodInterceptor());
 
UserService proxy = (UserService) enhancer.create();
proxy.addUser("张三");

2. 依赖

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

3. CGLIB 原理

CGLIB 通过字节码技术生成目标类的子类:
1. 继承目标类
2. 重写非 final 方法
3. 方法调用转发给 MethodInterceptor.intercept()

五、JDK vs CGLIB

对比项JDK 动态代理CGLIB
代理方式实现接口继承类
目标类要求必须实现接口不能是 final 类
方法要求接口方法非 final 方法
性能较慢较快
Spring 默认接口优先 JDK类用 CGLIB

Spring 配置

// Spring Boot 默认使用 CGLIB
// 可强制使用 JDK 代理
@SpringBootApplication(proxyBeanMethods = false)
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
 
// 或通过配置
spring.aop.proxy-target-class=true  // 强制 CGLIB
spring.aop.proxy-target-class=false // JDK 代理

六、实际应用

1. Spring AOP

@Aspect
@Component
public class LogAspect {
    
    @Before("execution(* com.example.service.*.*(..))")
    public void before(JoinPoint joinPoint) {
        System.out.println("方法执行前: " + joinPoint.getSignature());
    }
    
    @After("execution(* com.example.service.*.*(..))")
    public void after(JoinPoint joinPoint) {
        System.out.println("方法执行后: " + joinPoint.getSignature());
    }
    
    @Around("execution(* com.example.service.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long end = System.currentTimeMillis();
        System.out.println("执行时间: " + (end - start) + "ms");
        return result;
    }
}

2. MyBatis Mapper

// Mapper 接口
public interface UserMapper {
    User selectById(Long id);
    int insert(User user);
}
 
// MyBatis 使用 JDK 动态代理生成 Mapper 实现类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// userMapper 是代理对象

3. RPC 远程调用

// Dubbo 服务引用
@Reference
private UserService userService;
 
// userService 是代理对象,实际调用远程服务

4. 数据库连接池

// Connection 代理
public class ConnectionProxy implements Connection {
    private Connection realConnection;
    private DataSource dataSource;
    
    @Override
    public void close() throws SQLException {
        // 不关闭连接,而是归还连接池
        dataSource.returnConnection(this);
    }
    
    // 其他方法委托给 realConnection
}

七、面试高频问题

Q1: JDK 动态代理为什么必须实现接口?

JDK 动态代理生成的类继承 Proxy 类,由于 Java 单继承限制,只能通过实现接口的方式代理。

Q2: CGLIB 为什么不能代理 final 方法?

CGLIB 通过继承重写方法实现代理,final 方法无法被重写。

Q3: 代理模式与装饰器模式的区别?

模式目的关系
代理模式控制访问代理与目标对象生命周期相同
装饰器模式增强功能可动态添加多个装饰器

Q4: Spring AOP 用什么代理?

有接口 → 默认 JDK 动态代理
无接口 → CGLIB
可配置强制使用 CGLIB

Q5: 如何选择 JDK 还是 CGLIB?

  • 目标类实现接口 → JDK(更轻量)
  • 目标类无接口 → CGLIB
  • 需要代理类方法 → CGLIB
  • Spring Boot 默认 CGLIB(配置简单)

八、最佳实践

1. 通用动态代理工具

public class ProxyFactory {
    
    // JDK 动态代理
    public static <T> T createJdkProxy(T target, InvocationHandler handler) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            handler
        );
    }
    
    // CGLIB 代理
    public static <T> T createCglibProxy(Class<T> targetClass, MethodInterceptor interceptor) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(interceptor);
        return (T) enhancer.create();
    }
}

2. 选择合适的代理方式

// 判断是否有接口
if (targetClass.getInterfaces().length > 0) {
    // 使用 JDK 动态代理
} else {
    // 使用 CGLIB
}

3. 注意性能影响

  • 动态代理有一定性能开销
  • 热点代码考虑缓存代理对象
  • 使用 CGLIB 提升性能

更新时间:2026年3月16日