1. 动态代理技术概述
动态代理是Java编程中一项强大的技术,它允许我们在运行时创建代理对象,而无需手动编写代理类。这项技术在Spring AOP、RPC框架、事务管理等诸多领域都有广泛应用。作为Java开发者,深入理解动态代理机制对于掌握企业级开发至关重要。
动态代理主要分为两种实现方式:JDK原生动态代理和CGLIB动态代理。这两种方式各有特点,适用于不同的场景。JDK动态代理基于接口实现,而CGLIB则通过继承方式实现。理解它们的区别和适用场景,能够帮助我们在实际开发中做出更合理的技术选型。
在实际项目中,动态代理最常见的应用场景包括:日志记录、性能监控、事务管理、安全控制等横切关注点(cross-cutting concerns)的实现。通过动态代理,我们可以将这些与业务逻辑无关的代码从核心业务逻辑中分离出来,实现更清晰的代码结构和更高的可维护性。
2. 代理模式基础
2.1 代理模式的核心概念
代理模式是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式的核心思想是通过引入一个代理对象,在客户端和目标对象之间起到中介作用,从而可以在不修改目标对象代码的情况下,增加额外的功能或控制访问。
代理模式通常包含三个关键角色:
- 抽象主题(Subject):定义真实主题和代理主题的共同接口
- 真实主题(RealSubject):实现业务逻辑的实际对象
- 代理主题(Proxy):持有真实主题的引用,控制对真实主题的访问
2.2 静态代理的实现
静态代理是最基础的代理实现方式,它需要开发者手动编写代理类。下面是一个完整的静态代理示例:
java复制// 抽象主题接口
interface Image {
void display();
}
// 真实主题类
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading image: " + filename);
}
@Override
public void display() {
System.out.println("Displaying image: " + filename);
}
}
// 代理类
class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
// 可以在调用真实对象前后添加额外逻辑
System.out.println("Proxy: Before displaying image");
realImage.display();
System.out.println("Proxy: After displaying image");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");
// 图像将从磁盘加载
image.display();
System.out.println("");
// 图像不需要从磁盘加载
image.display();
}
}
静态代理的主要缺点是:
- 代理类和真实类必须实现相同的接口,导致接口变动时代理类也需要同步修改
- 每个真实类都需要一个对应的代理类,当代理逻辑相似时会造成大量重复代码
- 代理类在编译时就已经确定,缺乏灵活性
3. JDK动态代理详解
3.1 JDK动态代理的实现原理
JDK动态代理是Java标准库提供的代理实现,它基于接口和反射机制在运行时动态生成代理类。与静态代理不同,JDK动态代理不需要为每个被代理类手动编写代理类,而是通过java.lang.reflect.Proxy类在运行时动态生成。
JDK动态代理的核心组件包括:
InvocationHandler接口:定义代理对象的调用处理逻辑Proxy类:用于创建动态代理类和实例
3.2 完整实现示例
下面是一个完整的JDK动态代理实现示例,展示了如何为数据库操作添加事务管理功能:
java复制// 数据库操作接口
interface UserRepository {
void save(User user);
User findById(Long id);
}
// 真实主题类
class UserRepositoryImpl implements UserRepository {
@Override
public void save(User user) {
System.out.println("Saving user: " + user);
// 实际数据库操作...
}
@Override
public User findById(Long id) {
System.out.println("Finding user by id: " + id);
// 实际数据库查询...
return new User(id, "John Doe");
}
}
// 事务管理器
class TransactionManager {
public void begin() {
System.out.println("Transaction begin");
}
public void commit() {
System.out.println("Transaction commit");
}
public void rollback() {
System.out.println("Transaction rollback");
}
}
// 事务处理器
class TransactionHandler implements InvocationHandler {
private final Object target;
private final TransactionManager transactionManager;
public TransactionHandler(Object target, TransactionManager transactionManager) {
this.target = target;
this.transactionManager = transactionManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
transactionManager.begin();
try {
result = method.invoke(target, args);
transactionManager.commit();
} catch (Exception e) {
transactionManager.rollback();
throw e;
}
return result;
}
}
// 客户端代码
public class JdkProxyDemo {
public static void main(String[] args) {
// 创建真实对象
UserRepository realRepository = new UserRepositoryImpl();
// 创建事务管理器
TransactionManager transactionManager = new TransactionManager();
// 创建代理对象
UserRepository proxy = (UserRepository) Proxy.newProxyInstance(
realRepository.getClass().getClassLoader(),
realRepository.getClass().getInterfaces(),
new TransactionHandler(realRepository, transactionManager)
);
// 使用代理对象
User user = new User(1L, "Alice");
proxy.save(user);
User foundUser = proxy.findById(1L);
System.out.println("Found user: " + foundUser);
}
}
3.3 JDK动态代理的底层机制
JDK动态代理在运行时生成的代理类具有以下特点:
- 继承
java.lang.reflect.Proxy类 - 实现被代理接口的所有方法
- 每个方法调用都会转发到
InvocationHandler的invoke方法
我们可以通过以下工具类将生成的代理类保存到磁盘,以便分析其实现细节:
java复制public class ProxyClassDumper {
public static void dumpProxyClass(String path, String proxyName, Class<?>... interfaces) {
byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
try (FileOutputStream out = new FileOutputStream(path + proxyName + ".class")) {
out.write(classFile);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ProxyClassDumper.dumpProxyClass(
"./",
"UserRepositoryProxy",
UserRepository.class
);
}
}
生成的代理类大致结构如下:
java复制public final class UserRepositoryProxy extends Proxy implements UserRepository {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("UserRepository").getMethod("save", Class.forName("User"));
m4 = Class.forName("UserRepository").getMethod("findById", Long.TYPE);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
} catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.getMessage());
}
}
public UserRepositoryProxy(InvocationHandler var1) {
super(var1);
}
public final void save(User var1) {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final User findById(long var1) {
try {
return (User)super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
// 省略equals、hashCode、toString等方法
}
3.4 JDK动态代理的优缺点
优点:
- 无需手动编写代理类,减少重复代码
- 代理逻辑集中在一个
InvocationHandler中,便于维护 - 运行时动态生成,更加灵活
缺点:
- 只能代理接口,不能代理类
- 反射调用有一定性能开销
- 对于final方法或类无法代理
4. CGLIB动态代理深入解析
4.1 CGLIB动态代理原理
CGLIB(Code Generation Library)是一个强大的高性能代码生成库,它通过继承目标类并在运行时生成子类来实现动态代理。与JDK动态代理不同,CGLIB不需要目标类实现接口,它可以直接代理普通类。
CGLIB的核心组件包括:
Enhancer类:用于创建动态代理MethodInterceptor接口:定义方法拦截逻辑CallbackFilter接口:用于为不同方法指定不同的拦截器
4.2 基本使用示例
下面是一个完整的CGLIB动态代理示例,展示了如何为商品服务添加缓存功能:
java复制// 商品服务类(不需要实现接口)
class ProductService {
public Product getProductById(Long id) {
System.out.println("Fetching product from database: " + id);
// 模拟数据库查询
return new Product(id, "Product " + id, 100.0);
}
public void updateProduct(Product product) {
System.out.println("Updating product in database: " + product);
// 模拟数据库更新
}
}
// 缓存拦截器
class CacheInterceptor implements MethodInterceptor {
private Map<Long, Product> cache = new HashMap<>();
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 只为getProductById方法添加缓存
if ("getProductById".equals(method.getName())) {
Long id = (Long) args[0];
if (cache.containsKey(id)) {
System.out.println("Fetching product from cache: " + id);
return cache.get(id);
}
Product product = (Product) proxy.invokeSuper(obj, args);
cache.put(id, product);
return product;
}
return proxy.invokeSuper(obj, args);
}
}
// 客户端代码
public class CglibProxyDemo {
public static void main(String[] args) {
// 创建Enhancer实例
Enhancer enhancer = new Enhancer();
// 设置父类(被代理类)
enhancer.setSuperclass(ProductService.class);
// 设置回调(拦截器)
enhancer.setCallback(new CacheInterceptor());
// 创建代理对象
ProductService proxy = (ProductService) enhancer.create();
// 使用代理对象
Product p1 = proxy.getProductById(1L);
System.out.println("First fetch: " + p1);
Product p2 = proxy.getProductById(1L);
System.out.println("Second fetch: " + p2);
proxy.updateProduct(new Product(1L, "Updated Product", 150.0));
}
}
4.3 多拦截器与过滤器
CGLIB支持为同一个代理类配置多个拦截器,并通过CallbackFilter为不同方法指定不同的拦截逻辑。下面是一个高级示例:
java复制// 日志拦截器
class LoggingInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After method: " + method.getName());
return result;
}
}
// 性能监控拦截器
class PerformanceInterceptor 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 elapsed = System.currentTimeMillis() - start;
System.out.println(method.getName() + " executed in " + elapsed + "ms");
return result;
}
}
// 回调过滤器
class ProductServiceCallbackFilter implements CallbackFilter {
@Override
public int accept(Method method) {
if (method.getName().startsWith("get")) {
return 0; // 使用第一个拦截器(日志)
} else {
return 1; // 使用第二个拦截器(性能)
}
}
}
// 客户端代码
public class CglibAdvancedDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProductService.class);
// 设置多个回调
enhancer.setCallbacks(new Callback[] {
new LoggingInterceptor(),
new PerformanceInterceptor(),
NoOp.INSTANCE
});
// 设置回调过滤器
enhancer.setCallbackFilter(new ProductServiceCallbackFilter());
ProductService proxy = (ProductService) enhancer.create();
// get方法会使用LoggingInterceptor
Product p = proxy.getProductById(1L);
// update方法会使用PerformanceInterceptor
proxy.updateProduct(new Product(1L, "New Product", 200.0));
}
}
4.4 CGLIB动态代理的优缺点
优点:
- 可以代理普通类,不要求实现接口
- 性能通常优于JDK动态代理(特别是Java 8之前)
- 提供更丰富的功能,如方法过滤、多拦截器等
缺点:
- 无法代理final类或final方法
- 生成的代理类会继承目标类,可能导致某些继承问题
- 需要引入额外的依赖(CGLIB库)
5. JDK动态代理与CGLIB对比与选型
5.1 技术对比
| 特性 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|
| 实现方式 | 基于接口 | 基于继承 |
| 代理目标 | 只能代理接口 | 可以代理类和接口 |
| 性能 | 反射调用,较慢(Java 8前) | 直接调用,较快 |
| 依赖 | Java标准库 | 需要额外引入CGLIB库 |
| final方法/类 | 无影响 | 无法代理 |
| 方法过滤 | 不支持 | 支持 |
| 多拦截器 | 不支持 | 支持 |
5.2 选型建议
在实际项目中,选择哪种动态代理技术应考虑以下因素:
-
目标对象类型:
- 如果目标对象实现了接口,优先考虑JDK动态代理
- 如果目标对象没有实现接口,必须使用CGLIB
-
性能要求:
- 在Java 8及以后版本中,JDK动态代理的性能已经大幅提升
- 对于极高性能要求的场景,CGLIB可能仍有优势
-
功能需求:
- 如果需要方法过滤或多拦截器功能,选择CGLIB
- 如果只需要基本的代理功能,两者都可以
-
项目环境:
- 如果不想引入额外依赖,使用JDK动态代理
- 如果已经使用了Spring等框架(它们自带CGLIB),可以考虑使用CGLIB
Spring框架在内部同时使用了JDK动态代理和CGLIB。当被代理的bean实现了接口时,Spring默认使用JDK动态代理;否则使用CGLIB。从Spring 3.2开始,CGLIB已经包含在spring-core模块中,无需额外引入。
5.3 性能优化建议
- 缓存代理对象:动态代理对象的创建有一定开销,应尽量避免重复创建
- 减少反射调用:在JDK动态代理中,可以通过方法缓存优化性能
- 选择性代理:只为真正需要增强的方法创建代理,减少不必要的开销
- 考虑AspectJ:对于性能极其敏感的场合,可以考虑使用编译时织入的AspectJ
6. 动态代理在实际项目中的应用
6.1 Spring AOP中的动态代理
Spring AOP是动态代理技术的典型应用。理解Spring如何选择和使用动态代理,有助于我们更好地使用和调试AOP功能。
Spring AOP的代理创建规则:
- 如果目标对象实现了至少一个接口,使用JDK动态代理
- 如果目标对象没有实现任何接口,使用CGLIB
- 可以通过配置强制使用CGLIB(@EnableAspectJAutoProxy(proxyTargetClass = true))
示例:Spring AOP中的代理配置
java复制@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // 强制使用CGLIB
public class AppConfig {
// 配置bean...
}
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before method: " + joinPoint.getSignature().getName());
}
}
6.2 MyBatis中的动态代理
MyBatis的Mapper接口也是通过JDK动态代理实现的。当我们调用Mapper接口方法时,实际上调用的是MyBatis生成的代理对象。
MyBatis代理机制的特点:
- 每个Mapper接口都有一个对应的代理对象
- 代理对象将方法调用转换为SQL执行
- 通过
MapperProxy实现InvocationHandler接口
6.3 RPC框架中的动态代理
RPC(远程过程调用)框架通常使用动态代理来隐藏网络通信细节。客户端调用本地接口方法时,代理对象会将调用转换为网络请求。
RPC代理的典型实现:
- 定义服务接口
- 客户端通过动态代理创建接口的代理对象
- 代理对象将方法调用序列化为网络请求
- 服务端接收请求并执行实际方法
- 将结果返回给客户端
示例:简易RPC代理实现
java复制// RPC客户端代理工厂
public class RpcProxyFactory {
public static <T> T create(Class<T> interfaceClass, String url) {
return (T) Proxy.newProxyInstance(
interfaceClass.getClassLoader(),
new Class<?>[]{interfaceClass},
new RpcInvocationHandler(url)
);
}
}
// RPC调用处理器
class RpcInvocationHandler implements InvocationHandler {
private final String url;
public RpcInvocationHandler(String url) {
this.url = url;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 构造请求
RpcRequest request = new RpcRequest();
request.setInterfaceName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameterTypes(method.getParameterTypes());
request.setParameters(args);
// 发送网络请求
RpcResponse response = sendRequest(url, request);
// 处理响应
if (response.hasError()) {
throw response.getError();
}
return response.getResult();
}
private RpcResponse sendRequest(String url, RpcRequest request) {
// 实际网络通信逻辑...
return new RpcResponse();
}
}
7. 动态代理的高级应用与最佳实践
7.1 链式代理
在实际开发中,我们有时需要对同一个对象应用多个代理,形成代理链。这可以通过组合多个InvocationHandler或MethodInterceptor来实现。
示例:链式代理实现
java复制// 第一个拦截器:日志
class LoggingInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Logging before: " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("Logging after: " + method.getName());
return result;
}
}
// 第二个拦截器:性能监控
class PerformanceInterceptor 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 duration = System.currentTimeMillis() - start;
System.out.println(method.getName() + " executed in " + duration + "ms");
return result;
}
}
// 链式代理工厂
public class ChainedProxyFactory {
public static <T> T createProxy(Class<T> targetClass, MethodInterceptor... interceptors) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
if (interceptors.length == 1) {
enhancer.setCallback(interceptors[0]);
} else {
enhancer.setCallback(new ChainedInterceptor(interceptors));
}
return (T) enhancer.create();
}
static class ChainedInterceptor implements MethodInterceptor {
private final MethodInterceptor[] interceptors;
private int index = 0;
public ChainedInterceptor(MethodInterceptor[] interceptors) {
this.interceptors = interceptors;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (index < interceptors.length) {
return interceptors[index++].intercept(obj, method, args, proxy);
}
return proxy.invokeSuper(obj, args);
}
}
}
// 使用示例
public class ChainDemo {
public static void main(String[] args) {
ProductService proxy = ChainedProxyFactory.createProxy(
ProductService.class,
new LoggingInterceptor(),
new PerformanceInterceptor()
);
proxy.getProductById(1L);
}
}
7.2 动态代理的性能优化
动态代理虽然强大,但不当使用可能导致性能问题。以下是一些优化建议:
- 缓存代理对象:代理对象的创建成本较高,应尽可能重用
- 减少反射调用:在JDK动态代理中,可以缓存Method对象
- 选择性代理:只为需要增强的方法创建代理
- 使用MethodProxy:在CGLIB中,
MethodProxy比直接反射调用更快 - 考虑AspectJ:对于性能关键路径,可以使用编译时织入
示例:优化后的InvocationHandler
java复制public class OptimizedHandler implements InvocationHandler {
private final Object target;
private final Map<Method, MethodHandler> methodHandlers = new ConcurrentHashMap<>();
public OptimizedHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodHandler handler = methodHandlers.computeIfAbsent(method, this::createHandler);
return handler.handle(args);
}
private MethodHandler createHandler(Method method) {
// 根据方法特性创建不同的处理器
if (method.getName().startsWith("get")) {
return new CacheableMethodHandler(method);
}
return new DefaultMethodHandler(method);
}
interface MethodHandler {
Object handle(Object[] args) throws Throwable;
}
class DefaultMethodHandler implements MethodHandler {
private final Method method;
DefaultMethodHandler(Method method) {
this.method = method;
}
@Override
public Object handle(Object[] args) throws Throwable {
long start = System.nanoTime();
Object result = method.invoke(target, args);
long duration = System.nanoTime() - start;
System.out.println(method.getName() + " executed in " + duration + "ns");
return result;
}
}
class CacheableMethodHandler implements MethodHandler {
private final Method method;
private final Map<Object, Object> cache = new HashMap<>();
CacheableMethodHandler(Method method) {
this.method = method;
}
@Override
public Object handle(Object[] args) throws Throwable {
Object key = args != null && args.length > 0 ? args[0] : "default";
if (cache.containsKey(key)) {
return cache.get(key);
}
Object result = method.invoke(target, args);
cache.put(key, result);
return result;
}
}
}
7.3 动态代理的线程安全问题
动态代理本身是线程安全的,但需要注意:
- 无状态拦截器:确保
InvocationHandler或MethodInterceptor是无状态的,或者有适当的状态管理 - 共享资源同步:如果拦截器使用共享资源(如缓存),需要适当的同步机制
- 代理对象重用:通常代理对象可以被多个线程安全地共享
7.4 动态代理的调试技巧
调试动态代理代码可能会遇到一些挑战,以下技巧可以帮助:
- 查看代理类:使用
ProxyGenerator或CGLIB工具生成代理类源代码 - 日志记录:在
InvocationHandler或MethodInterceptor中添加详细的日志 - 断点设置:在代理方法和拦截器中设置断点
- 代理对象识别:使用
Proxy.isProxyClass()或Enhancer.isEnhanced()检查对象是否为代理
8. 常见问题与解决方案
8.1 代理对象方法调用问题
问题:在代理对象方法内部调用另一个代理方法时,增强逻辑不生效
原因:当代理对象方法内部调用另一个方法时,调用的是this.method(),而不是代理对象的method()
解决方案:
- 通过AopContext获取当前代理对象(需要暴露代理)
- 将内部调用重构为通过代理对象调用
示例:
java复制@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class AppConfig {
// 配置...
}
@Service
public class OrderService {
public void placeOrder(Order order) {
// 其他逻辑...
this.validateOrder(order); // 增强不会生效
((OrderService) AopContext.currentProxy()).validateOrder(order); // 增强会生效
}
public void validateOrder(Order order) {
// 验证逻辑...
}
}
8.2 循环依赖问题
问题:当代理对象之间存在循环依赖时,可能导致启动失败
解决方案:
- 使用setter注入代替构造器注入
- 使用
@Lazy注解延迟初始化 - 重构设计,消除循环依赖
8.3 代理对象序列化问题
问题:JDK动态代理对象序列化后再反序列化可能失败
原因:生成的代理类没有实现Serializable接口
解决方案:
- 使用CGLIB代理(生成的类默认实现
Serializable) - 自定义
InvocationHandler并实现Serializable - 避免序列化代理对象,改为序列化真实对象
8.4 性能调优问题
问题:动态代理导致性能下降
优化建议:
- 缓存代理对象
- 减少不必要的代理
- 使用
MethodProxy代替反射调用(CGLIB) - 对于高频调用方法,考虑直接编码增强逻辑
9. 动态代理的替代方案
虽然动态代理功能强大,但在某些场景下可能有更合适的替代方案:
9.1 静态AOP(AspectJ)
适用场景:
- 性能要求极高的场合
- 需要代理final类或方法
- 编译时织入可以接受的场景
特点:
- 编译时生成增强代码
- 零运行时开销
- 需要特殊的编译器或构建工具支持
9.2 手动代理
适用场景:
- 代理逻辑简单
- 需要极致性能
- 代理关系固定不变
特点:
- 代码直观,易于调试
- 维护成本高
- 缺乏灵活性
9.3 字节码增强(Byte Buddy, Javassist)
适用场景:
- 需要比CGLIB更灵活的字节码操作
- 特殊的需求(如接口混合、字段添加等)
特点:
- 比CGLIB更现代的API
- 更强的灵活性
- 学习曲线较陡
10. 实际项目经验分享
在实际企业级项目中应用动态代理时,我总结了以下几点经验:
-
明确代理边界:不要过度使用代理,清晰界定哪些功能应该通过代理实现,哪些应该放在业务逻辑中
-
保持拦截器轻量:拦截器中的逻辑应该尽可能简单,避免复杂的业务逻辑和IO操作
-
注意代理顺序:当多个代理或拦截器同时作用时,执行顺序可能影响行为,需要明确约定
-
统一异常处理:在拦截器中统一处理异常,避免异常传播导致代理链中断
-
性能监控:对关键路径的代理逻辑进行性能监控,及时发现潜在问题
-
文档记录:为代理逻辑和维护良好的文档,说明代理的目的、范围和注意事项
-
测试覆盖:为代理逻辑编写专门的测试用例,包括边界条件和异常场景
-
避免过度抽象:虽然动态代理很强大,但不要为了使用而使用,确保它确实解决了实际问题