代理模式
代理模式为其他对象提供一种代理以控制对这个对象的访问。
一、核心概念
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
可配置强制使用 CGLIBQ5: 如何选择 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日