1. 责任链模式的核心思想与应用场景
责任链模式(Chain of Responsibility Pattern)是行为型设计模式中的一种经典实现,它通过将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求。在Spring框架中,这种模式被广泛应用于拦截器链、过滤器链等场景,而AdvisorChain正是这种模式的一个典型实现案例。
责任链模式的核心工作原理可以类比现实生活中的审批流程:当一个申请提交后,它会依次经过多个审批层级,每个层级都有权处理或传递该申请。在代码层面,这意味着:
- 每个处理器(Handler)都持有对下一个处理器的引用
- 请求会沿着这条链传递,直到被某个处理器处理
- 处理器之间相互独立,可以动态调整顺序或增减处理器
在Spring AOP中,AdvisorChain的工作机制特别适合处理以下场景:
- 方法调用的前置/后置增强
- 环绕通知的执行
- 多个切面的有序执行
- 条件性拦截控制
提示:责任链模式的关键优势在于它的灵活性——你可以随时添加、移除或重新排序处理节点,而不需要修改客户端代码或已有处理器逻辑。
2. AdvisorChain的架构设计与核心组件
Spring框架中的AdvisorChain实现了一套精妙的拦截器链机制,它主要由以下几个核心组件构成:
2.1 Advisor与Advice的关系
在Spring AOP中,Advisor是连接Pointcut和Advice的桥梁。一个典型的Advisor实现包含:
java复制public interface Advisor {
Advice getAdvice();
boolean isPerInstance();
}
而Advice则定义了具体的增强行为,常见的类型包括:
- MethodBeforeAdvice:方法前置增强
- AfterReturningAdvice:方法后置增强
- MethodInterceptor:方法环绕增强
2.2 拦截器链的构建过程
当代理对象的方法被调用时,Spring会按以下步骤构建拦截器链:
- 获取所有适用的Advisor
- 将Advisor转换为MethodInterceptor
- 按优先级排序拦截器
- 创建拦截器链并执行
关键的转换逻辑通常体现在DefaultAdvisorChainFactory中:
java复制public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
// 1. 从配置中获取所有Advisor
List<Advisor> advisors = config.getAdvisors();
// 2. 创建拦截器列表
List<Object> interceptorList = new ArrayList<>(advisors.size());
// 3. 遍历Advisor并筛选适用的
for (Advisor advisor : advisors) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
if (pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
if (mm.matches(method, targetClass)) {
interceptorList.add(advisor.getAdvice());
}
}
}
}
return interceptorList;
}
2.3 链式执行的核心机制
拦截器链的执行依赖于ReflectiveMethodInvocation类,它维护了当前拦截器的索引位置,并通过递归调用实现链式传递:
java复制public class ReflectiveMethodInvocation implements ProxyMethodInvocation {
private final Object proxy;
private final Object target;
private final Method method;
private final Object[] arguments;
private final List<Object> interceptorsAndDynamicMethodMatchers;
private int currentInterceptorIndex = -1;
public Object proceed() throws Throwable {
// 如果已经执行到链尾,则调用原始方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 获取下一个拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 执行拦截器逻辑
if (interceptorOrInterceptionAdvice instanceof MethodInterceptor) {
MethodInterceptor mi = (MethodInterceptor) interceptorOrInterceptionAdvice;
return mi.invoke(this);
}
// ... 其他类型处理
}
}
3. AdvisorChain的源码深度解析
3.1 链式调用的实现细节
Spring AOP中的拦截器链执行过程实际上是一个递归调用过程。让我们通过一个具体的例子来说明:
假设我们有三个拦截器:日志拦截器、性能监控拦截器和事务拦截器。它们的执行顺序如下:
- 日志拦截器(前置处理)
- 性能监控拦截器(开始计时)
- 事务拦截器(开启事务)
- 执行目标方法
- 事务拦截器(提交事务)
- 性能监控拦截器(结束计时并记录)
- 日志拦截器(后置处理)
这个过程的代码级实现体现在MethodInterceptor接口的invoke方法中:
java复制public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
一个典型的环绕拦截器实现如下:
java复制public class LoggingInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
// 前置处理
System.out.println("Before method: " + invocation.getMethod().getName());
try {
// 执行链中的下一个拦截器或目标方法
Object result = invocation.proceed();
// 后置处理
System.out.println("After method: " + invocation.getMethod().getName());
return result;
} catch (Exception e) {
// 异常处理
System.out.println("Exception in method: " + e.getMessage());
throw e;
}
}
}
3.2 动态代理与拦截器链的协作
Spring AOP默认使用JDK动态代理或CGLIB来创建代理对象。当代理方法被调用时,会触发InvocationHandler的invoke方法:
java复制public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 创建方法调用对象
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 执行拦截器链
return invocation.proceed();
}
}
这个过程中有几个关键点值得注意:
- 拦截器链是按需构建的,只有在方法第一次被调用时才会完整构建
- 拦截器的排序基于@Order注解或Ordered接口实现
- 链式调用是通过MethodInvocation维护当前索引位置实现的
3.3 性能优化与缓存机制
Spring对拦截器链进行了多层次的缓存优化:
- Advisor缓存:配置后Advisor列表会被缓存,避免重复计算
- 方法匹配缓存:对每个方法会缓存匹配的拦截器列表
- 拦截器实例缓存:单例拦截器会被复用
核心缓存逻辑体现在AdvisedSupport类中:
java复制public class AdvisedSupport extends ProxyConfig implements Advised {
private transient Map<MethodCacheKey, List<Object>> methodCache;
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
}
4. 实战中的问题排查与性能调优
4.1 常见问题排查指南
在实际使用AdvisorChain时,可能会遇到以下典型问题:
-
拦截器顺序不符合预期
- 检查各个Advisor是否实现了Ordered接口或使用了@Order注解
- 确认配置的加载顺序是否正确
- 注意AspectJ注解的继承规则
-
拦截器未生效
- 确认Pointcut表达式是否正确匹配目标方法
- 检查代理对象是否创建成功
- 验证目标类是否被正确扫描
-
循环调用问题
- 避免在拦截器中调用被代理的同类方法
- 使用AopContext.currentProxy()获取当前代理对象
4.2 性能调优建议
对于高性能场景下的AdvisorChain使用,可以考虑以下优化策略:
-
减少动态方法匹配
- 尽量使用静态Pointcut而非动态Pointcut
- 避免在匹配逻辑中使用复杂表达式
-
合理使用缓存
- 对于频繁调用的方法,考虑缓存拦截器链
- 对无状态拦截器使用单例模式
-
控制拦截器数量
- 合并功能相似的拦截器
- 避免在拦截器中执行耗时操作
-
选择性代理
- 只为真正需要拦截的方法创建代理
- 使用懒加载策略初始化拦截器
4.3 监控与诊断技巧
为了更好地理解和监控AdvisorChain的行为,可以采用以下诊断方法:
- 日志追踪
java复制@Bean
public AdvisorChainMonitor advisorChainMonitor() {
return new AdvisorChainMonitor();
}
public static class AdvisorChainMonitor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
long start = System.nanoTime();
try {
return invocation.proceed();
} finally {
long duration = System.nanoTime() - start;
System.out.println("Interceptor chain execution time: " + duration + "ns");
}
}
}
- Spring Boot Actuator集成
properties复制# application.properties
management.endpoints.web.exposure.include=*
management.endpoint.beans.enabled=true
- 可视化工具
- 使用Spring Boot Admin监控AOP代理情况
- 通过JConsole或VisualVM观察代理对象结构
在实际项目中,我发现AdvisorChain的性能瓶颈往往出现在动态Pointcut匹配和过多的拦截器嵌套上。一个经验法则是:保持拦截器链尽可能简洁,将不必要的前置/后置检查移到业务方法内部,只在真正需要横切关注点的地方使用AOP。
