代理模式实战详解
代理模式是最常用的设计模式,Spring AOP、RPC 框架都基于代理实现。
一、代理模式基础
1.1 什么是代理
代理模式 = 为对象提供一种代理以控制对这个对象的访问
角色:
- 抽象主题(Subject):接口
- 真实主题(RealSubject):实际对象
- 代理(Proxy):代理对象
1.2 应用场景
1. 远程代理(RPC)
2. 虚拟代理(延迟加载)
3. 安全代理(权限控制)
4. 智能代理(日志、缓存)
5. AOP(事务、日志)
二、静态代理
2.1 实现
// 抽象主题
interface UserService {
void addUser(String name);
}
// 真实主题
class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("Adding user: " + name);
}
}
// 代理
class UserServiceProxy implements UserService {
private UserService target;
public UserServiceProxy(UserService target) {
this.target = target;
}
@Override
public void addUser(String name) {
System.out.println("Before addUser");
target.addUser(name);
System.out.println("After addUser");
}
}
// 使用
UserService target = new UserServiceImpl();
UserService proxy = new UserServiceProxy(target);
proxy.addUser("Alice");
2.2 优缺点
优点:
- 简单易懂
- 编译期检查
缺点:
- 每个类需要单独代理
- 代码重复
- 难以维护
三、JDK 动态代理
3.1 实现原理
// 核心类
Proxy.newProxyInstance(
ClassLoader loader, // 类加载器
Class<?>[] interfaces, // 接口数组
InvocationHandler h // 调用处理器
)
3.2 实现示例
// 接口
interface UserService {
void addUser(String name);
void deleteUser(Long id);
}
// 实现类
class UserServiceImpl implements UserService {
@Override
public void addUser(String name) {
System.out.println("Adding user: " + name);
}
@Override
public void deleteUser(Long id) {
System.out.println("Deleting user: " + id);
}
}
// 代理工厂
public class ProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("Before: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After: " + method.getName());
return result;
}
);
}
}
// 使用
UserService target = new UserServiceImpl();
UserService proxy = ProxyFactory.createProxy(target);
proxy.addUser("Alice");
proxy.deleteUser(1L);
3.3 InvocationHandler
// 自定义 InvocationHandler
public class LoggingHandler implements InvocationHandler {
private final Object target;
public LoggingHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long cost = System.currentTimeMillis() - start;
System.out.println(method.getName() + " cost: " + cost + "ms");
return result;
}
}
// 使用
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LoggingHandler(target)
);
四、CGLIB 动态代理
4.1 为什么需要 CGLIB
JDK 动态代理限制:
- 只能代理接口
- 不能代理类
CGLIB 优势:
- 可以代理类
- 通过继承实现
4.2 实现示例
// 添加依赖
// <dependency>
// <groupId>cglib</groupId>
// <artifactId>cglib</artifactId>
// <version>3.3.0</version>
// </dependency>
// 目标类(无接口)
class UserService {
public void addUser(String name) {
System.out.println("Adding user: " + name);
}
public void deleteUser(Long id) {
System.out.println("Deleting user: " + id);
}
}
// 代理工厂
public class CglibProxyFactory {
@SuppressWarnings("unchecked")
public static <T> T createProxy(T target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("Before: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After: " + method.getName());
return result;
});
return (T) enhancer.create();
}
}
// 使用
UserService target = new UserService();
UserService proxy = CglibProxyFactory.createProxy(target);
proxy.addUser("Alice");
4.3 MethodInterceptor
// 自定义 MethodInterceptor
public class LoggingInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
long start = System.currentTimeMillis();
Object result = proxy.invokeSuper(obj, args);
long cost = System.currentTimeMillis() - start;
System.out.println(method.getName() + " cost: " + cost + "ms");
return result;
}
}
// 使用
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new LoggingInterceptor());
UserService proxy = (UserService) enhancer.create();
五、JDK vs CGLIB
5.1 对比
| 特性 | JDK 动态代理 | CGLIB |
|---|---|---|
| 实现方式 | 接口 | 继承 |
| 代理对象 | 接口实现类 | 任意类 |
| 性能 | 高 | 稍低 |
| 依赖 | JDK 自带 | 第三方库 |
| final 方法 | ❌ 不能代理 | ❌ 不能代理 |
| Spring 选择 | 有接口用 JDK | 无接口用 CGLIB |
5.2 选择建议
// ✅ 有接口:JDK 动态代理
interface UserService {}
class UserServiceImpl implements UserService {}
// Spring 默认使用 JDK 代理
// ✅ 无接口:CGLIB
class UserService {}
// Spring 强制 CGLIB
proxyFactory.setProxyTargetClass(true);
// ✅ 性能敏感:JDK 代理
// ✅ 类代理:CGLIB
六、Spring AOP
6.1 AOP 核心概念
Aspect(切面) = 横切关注点的模块化
Pointcut(切入点) = 匹配连接点的谓词
Advice(通知) = 切面在特定执行的动作
Joinpoint(连接点) = 程序执行的点(方法调用)
Weaving(织入) = 将切面应用到目标对象
6.2 Spring AOP 实现
// 切面定义
@Aspect
@Component
public class LoggingAspect {
// 切入点
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 前置通知
@Before("serviceMethods()")
public void before(JoinPoint joinPoint) {
System.out.println("Before: " + joinPoint.getSignature());
}
// 后置通知
@After("serviceMethods()")
public void after(JoinPoint joinPoint) {
System.out.println("After: " + joinPoint.getSignature());
}
// 环绕通知
@Around("serviceMethods()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
long cost = System.currentTimeMillis() - start;
System.out.println("Cost: " + cost + "ms");
return result;
}
// 返回通知
@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void afterReturning(Object result) {
System.out.println("Result: " + result);
}
// 异常通知
@AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
public void afterThrowing(Exception ex) {
System.out.println("Exception: " + ex.getMessage());
}
}
6.3 Spring 代理选择
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
// 默认:有接口用 JDK,无接口用 CGLIB
@Bean
public UserService userService() {
return new UserServiceImpl();
}
// 强制使用 CGLIB
@Bean
public ProxyFactoryBean proxyFactoryBean() {
ProxyFactoryBean factory = new ProxyFactoryBean();
factory.setProxyTargetClass(true); // 强制 CGLIB
return factory;
}
}
七、实战场景
7.1 事务代理
public class TransactionProxy {
public static <T> T createProxy(T target, DataSource dataSource) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false);
Object result = method.invoke(target, args);
conn.commit();
return result;
} catch (Exception e) {
conn.rollback();
throw e;
} finally {
conn.close();
}
}
);
}
}
7.2 权限代理
public class AuthProxy {
public static <T> T createProxy(T target, User currentUser) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
// 检查权限
if (!hasPermission(currentUser, method)) {
throw new SecurityException("No permission");
}
return method.invoke(target, args);
}
);
}
private boolean hasPermission(User user, Method method) {
// 检查权限逻辑
return true;
}
}
7.3 缓存代理
public class CacheProxy {
private final Map<String, Object> cache = new ConcurrentHashMap<>();
public <T> T createProxy(T target) {
return (T) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
// 生成缓存 key
String key = method.getName() + ":" + Arrays.toString(args);
// 读缓存
Object cached = cache.get(key);
if (cached != null) {
return cached;
}
// 执行方法
Object result = method.invoke(target, args);
// 写缓存
cache.put(key, result);
return result;
}
);
}
}
7.4 RPC 代理
public class RpcProxy {
@SuppressWarnings("unchecked")
public static <T> T createProxy(Class<T> interfaceClass, String host, int port) {
return (T) Proxy.newProxyInstance(
interfaceClass.getClassLoader(),
new Class[] { interfaceClass },
(proxy, method, args) -> {
// 网络调用
Socket socket = new Socket(host, port);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
// 发送请求
oos.writeUTF(method.getName());
oos.writeObject(args);
// 接收响应
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object result = ois.readObject();
return result;
}
);
}
}
八、总结
代理模式核心要点:
| 代理类型 | 实现方式 | 适用场景 |
|---|---|---|
| 静态代理 | 手动编写代理类 | 简单场景 |
| JDK 动态代理 | 接口 + InvocationHandler | 有接口 |
| CGLIB | 继承 + MethodInterceptor | 无接口 |
| Spring AOP | 注解 + 切面 | 企业应用 |
Spring AOP 默认有接口用 JDK,无接口用 CGLIB,理解代理原理对掌握框架至关重要。