Spring AOP 与 AspectJ 注解的幕后协作机制
幕后主角:AnnotationAwareAspectJAutoProxyCreator
Spring 在启动时会把所有 @Aspect 标注的类转化为可拦截的 Advisor,真正的幕后推手是 AnnotationAwareAspectJAutoProxyCreator。它继承自 AspectJAwareAdvisorAutoProxyCreator,内部额外持有两个关键组件:
AspectJAdvisorFactory:负责把切面方法解析成 Advisor。BeanFactoryAspectJAdvisorsBuilder:负责扫描并缓存所有切面 Bean。
发现候选 Advisor 的完整链路
findCandidateAdvisors() 在父类逻辑基础上追加 AspectJ 切面:
@Override
protected List<Advisor> findCandidateAdvisors() {
List<Advisor> all = super.findCandidateAdvisors(); // Spring 原生 Advisor
if (aspectJBuilder != null) {
all.addAll(aspectJBuilder.buildAspectJAdvisors()); // 追加 AspectJ 产生的 Advisor
}
return all;
}
buildAspectJAdvisors() 用双重检查锁一次性扫描所有 Bean,流程如下:
- 通过
BeanFactoryUtils.beanNamesForTypeIncludingAncestors拿到全部 Bean 名称。 - 过滤出类上有
@Aspect且未被 ajc 编译过的类型。 - 根据
@Aspect的 per-clause 决定是单例还是原型:- 单例:立即生成 Advisor 并缓存。
- 原型:缓存
PrototypeAspectInstanceFactory,每次新建 Advisor。
- 后续调用直接走缓存,避免重复反射。
从切面类到 Advisor 的转换
ReflectiveAspectJAdvisorFactory#getAdvisors() 负责把切面类拆成多条 Advisor:
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory factory) {
List<Advisor> result = new ArrayList<>();
Class<?> aspect = factory.getAspectMetadata().getAspectClass();
// 1. 处理普通增强方法
for (Method m : getAdvisorMethods(aspect)) {
result.add(makeAdvisor(m, factory));
}
// 2. 处理接口引入字段
for (Field f : aspect.getDeclaredFields()) {
Advisor introduction = makeIntroduction(f);
if (introduction != null) result.add(introduction);
}
return result;
}
getAdvisorMethods() 会排除 @Pointcut 标注的方法,并按注解顺序排序。
把方法变成 Advice
makeAdvisor() 的核心是 InstantiationModelAwarePointcutAdvisorImpl,它内部再调用 ReflectiveAspectJAdvisorFactory#getAdvice():
private Advice toAdvice(Method method, AspectJExpressionPointcut pc, MetadataAwareAspectInstanceFactory factory) {
AspectJAnnotation<?> ann = findAnnotation(method);
return switch (ann.annotationType()) {
case AtAround -> new AspectJAroundAdvice(method, pc, factory);
case AtBefore -> new AspectJMethodBeforeAdvice(method, pc, factory);
case AtAfter -> new AspectJAfterAdvice(method, pc, factory);
case AtAfterReturning -> {
AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pc, factory);
advice.setReturningName(((AfterReturning) ann.getAnnotation()).returning());
yield advice;
}
case AtAfterThrowing -> {
AspectJAfterThrowingAdvice advice = new AspectJAfterThrowingAdvice(method, pc, factory);
advice.setThrowingName(((AfterThrowing) ann.getAnnotation()).throwing());
yield advice;
}
default -> throw new IllegalStateException("Unknown advice type");
};
}
接口引入(Introduction)实战
@DeclareParents 可以把任意类伪装成新接口:
@Aspect
public class AuditAspect {
@DeclareParents(
value = "com.demo.service.*",
defaultImpl = AuditableImpl.class)
private Auditable mixin;
}
运行时,Spring 会为匹配的 Bean 生成代理,并把 Auditable 的所有调用转发到 AuditableImpl 实例。
小结
Spring 并未真正使用 AspectJ 的编译器或织入器,而是复用了 AspectJ 的注解模型与切点表达式,通过 AnnotationAwareAspectJAutoProxyCreator 把 @Aspect 类转换成 Spring 自身的 Advisor 体系,从而在不改变字节码的前提下实现灵活且强大的切面编程。