☰
Spring AOP自动代理源码剖析:DefaultAdvisorAutoProxyCreator原理与实战
2026/10/13 5:22:22 网站建设 项目流程

1. 从一次线上故障说起:为什么我要啃 DefaultAdvisorAutoProxyCreator 的源码

去年帮一个朋友排查线上问题,场景很典型:他们项目里用了一个自定义注解,配合@Aspect做接口耗时统计,本地跑得好好的,一上预发环境就发现有些 Service 的耗时日志死活打不出来,但另外一些 Service 又正常。更诡异的是,同一个类,有的方法被代理了,有的方法没被代理。他第一反应是切面写错了,检查了半天@Pointcut表达式,没毛病。

我当时让他把spring.aop.auto和spring.aop.proxy-target-class这两个配置贴出来,再让他确认有没有手动注册过AnnotationAwareAspectJAutoProxyCreator或者DefaultAdvisorAutoProxyCreator。结果发现他们项目里同时存在两套自动代理的配置,一套是 Spring Boot 自动装配的,一套是某位前辈手动@Bean注册的DefaultAdvisorAutoProxyCreator,两者打架了。

这个问题最后就是靠读DefaultAdvisorAutoProxyCreator的源码定位的。它属于 Spring AOP 里"自动代理"这条线,和AnnotationAwareAspectJAutoProxyCreator是堂兄弟关系,但工作方式差别很大。很多人用 Spring AOP 用了好几年,只知道@Aspect能生效,却不知道背后是谁在什么时候把 Bean 替换成了代理对象。这篇就把DefaultAdvisorAutoProxyCreator从定位、原理、源码流程到实操配置、踩坑排查,完整地捋一遍。

这篇文章适合三类人看:一是已经会用 Spring AOP,但想搞清楚自动代理底层机制的开发者;二是正在做框架二次封装、需要自己控制代理创建时机的同学;三是被"AOP 不生效""代理对象类型转换异常"这类问题折磨过、想彻底搞明白的人。读完之后,你应该能自己判断一个 Bean 到底会不会被自动代理、被谁代理、代理成 JDK 动态代理还是 CGLIB。

2. DefaultAdvisorAutoProxyCreator 到底是个什么东西

2.1 它在 Spring AOP 体系里的位置

先把 Spring AOP 的代理创建方式分成两条路来看,这样定位更清楚。

第一条路是手动代理,典型代表是ProxyFactory。你自己 new 一个ProxyFactory,塞进目标对象和通知,调用getProxy()拿到代理对象。这种方式控制粒度最细,但每个需要代理的地方都得手写,业务代码侵入性强,实际项目里很少这么干。

第二条路是自动代理,核心思路是让 Spring 容器在 Bean 初始化完成后,自动判断这个 Bean 要不要被代理,要的话就悄悄把代理对象放进容器。自动代理又分两种实现风格:

  • 基于BeanPostProcessor的自动代理创建器,代表就是AbstractAutoProxyCreator及其子类,DefaultAdvisorAutoProxyCreator和AnnotationAwareAspectJAutoProxyCreator都在这条线上。
  • 基于BeanFactoryPostProcessor的,比如早期的一些实现,现在基本不用了。

DefaultAdvisorAutoProxyCreator继承自AbstractAdvisorAutoProxyCreator,而AbstractAdvisorAutoProxyCreator又继承自AbstractAutoProxyCreator。这条继承链决定了它的核心能力:它是一个BeanPostProcessor,会在每个 Bean 初始化后介入,扫描容器里所有的Advisor,判断当前 Bean 是否匹配某个 Advisor 的切点,匹配就创建代理。

用一句话概括它的职责:它不关心切面是怎么定义的,只关心容器里有哪些 Advisor,然后拿这些 Advisor 去匹配每一个 Bean,匹配上就代理。

2.2 和 AnnotationAwareAspectJAutoProxyCreator 的关键区别

这是最容易混淆的地方,我用一个表格对比清楚。

对比维度DefaultAdvisorAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreator
触发来源容器中所有 Advisor 类型的 Bean@Aspect注解标注的 Bean,内部转换成 Advisor
是否解析 @Aspect不解析解析,通过 AspectJ 表达式解析器
典型使用场景手动注册 Advisor Bean 的场景Spring Boot 默认自动装配的场景
注册方式通常手动@Bean注册AopAutoConfiguration自动注册
切点表达式支持取决于 Advisor 自身完整支持 AspectJ 切点表达式
优先级两者都是 Ordered,可调默认 Ordered.LOWEST_PRECEDENCE

关键点在于:AnnotationAwareAspectJAutoProxyCreator其实是DefaultAdvisorAutoProxyCreator的"增强版",它额外做了一件事——把@Aspect标注的 Bean 解析成 Advisor。如果你只注册了DefaultAdvisorAutoProxyCreator,那么@Aspect注解是不会被识别的,因为没人去解析它。

反过来,如果你两个都注册了,就会出现我朋友遇到的那种情况:两个创建器都在postProcessAfterInitialization里尝试代理同一个 Bean,谁先执行谁生效,后执行的看到已经是代理对象了,行为就变得不可预测。这就是为什么 Spring Boot 场景下不要手动再注册DefaultAdvisorAutoProxyCreator。

2.3 它解决了什么实际问题

在没有自动代理的年代,要给一批 Bean 统一加事务、加日志、加权限校验,你得为每个 Bean 手动创建代理,或者写一堆重复的工厂代码。DefaultAdvisorAutoProxyCreator的价值在于声明式:你只需要把 Advisor 注册到容器里,剩下的匹配和代理它全包了。

举个实际例子。假设你有一套自研的限流框架,限流规则不是通过注解声明的,而是通过配置中心下发的,运行时动态生成一批Advisor注册到 Spring 容器。这种情况下@Aspect就不适用了,因为你没有注解可解析。这时候DefaultAdvisorAutoProxyCreator就是最合适的选择——它只认 Advisor,你把 Advisor 注册进去,它自动帮你把匹配的 Bean 代理掉。

再比如一些老系统做 AOP 迁移,历史代码里全是手动ProxyFactory,想统一改成自动代理,但又不想引入 AspectJ 那套注解体系,DefaultAdvisorAutoProxyCreator就是平滑过渡的桥梁。

3. 源码主线拆解:一个 Bean 是怎么被它代理的

3.1 继承链与核心方法入口

先把继承链摆出来,方便对照源码:

DefaultAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware, Ordered

AbstractAutoProxyCreator实现了BeanPostProcessor的两个核心方法,代理的入口就在这里:

  • postProcessBeforeInitialization:默认直接返回原 Bean,不做处理。
  • postProcessAfterInitialization:代理创建的主战场,Bean 初始化完成后调用。

postProcessAfterInitialization的逻辑大致是:先判断当前 Bean 是否在"已代理"缓存里,不在的话调用wrapIfNecessary尝试包装。wrapIfNecessary里做几件事:判断 Bean 是否是基础设施类(比如Advisor、Advice本身不代理)、是否已经代理过、获取匹配的 Advisor 列表、有匹配就创建代理。

这里有个细节值得说:AbstractAutoProxyCreator里维护了一个targetSourcedBeans和advisedBeans缓存。advisedBeans记录的是"这个 Bean 需不需要被代理"的判断结果,避免每次请求都重新匹配一遍切点。这个缓存是ConcurrentHashMap,key 是 Bean 名称,value 是 Boolean。如果你在运行时动态往容器里加 Advisor,已经判断过"不需要代理"的 Bean 不会重新判断,这是个隐藏的坑,后面排查章节会细说。

3.2 findCandidateAdvisors:Advisor 从哪来

AbstractAdvisorAutoProxyCreator重写了getAdvicesAndAdvisorsForBean,核心是findCandidateAdvisors方法。这个方法在DefaultAdvisorAutoProxyCreator里的实现非常直接:

protected List<Advisor> findCandidateAdvisors() { return this.beanFactory.getBeansOfType(Advisor.class).values() .stream().collect(Collectors.toList()); }

翻译成人话:从 BeanFactory 里捞出所有类型是 Advisor 的 Bean。注意是getBeansOfType,它会触发这些 Advisor Bean 的实例化。所以如果你的 Advisor 本身依赖了别的 Bean,这里会连带初始化,要小心循环依赖。

而AnnotationAwareAspectJAutoProxyCreator重写了这个方法,在调用父类逻辑的基础上,额外通过aspectJAdvisorsBuilder去解析@AspectBean,把切面里的通知方法包装成 Advisor 加进来。这就是两者最本质的分叉点。

3.3 findAdvisorsThatCanApply:切点匹配怎么做的

拿到所有候选 Advisor 后,AbstractAdvisorAutoProxyCreator会调用findAdvisorsThatCanApply做筛选。这个方法内部用AopUtils.findAdvisorsThatCanApply逐个判断 Advisor 的切点能否匹配当前 Bean 的类和方法。

匹配过程分两步:先做类级别的粗筛,用ClassFilter判断这个 Advisor 是否可能作用于当前类;粗筛通过后再做方法级别的细筛,用MethodMatcher遍历类里的方法,只要有一个方法匹配,这个 Advisor 就保留。

这里有个性能相关的点:方法级匹配会遍历类的所有方法,如果一个类方法特别多,或者 Advisor 特别多,这个匹配开销不小。不过 Spring 有缓存兜底,advisedBeans缓存了判断结果,同一个 Bean 只会判断一次。但如果你在开发环境频繁热部署,缓存会不断重建,启动会变慢,这是正常现象。

3.4 createProxy:代理对象是怎么造出来的

筛选出匹配的 Advisor 后,进入createProxy环节。这一步的核心逻辑在AbstractAutoProxyCreator.createProxy:

  1. 收集所有匹配的 Advisor 和 Advice。
  2. 构建ProxyFactory,设置目标对象、接口、拦截器链。
  3. 根据配置决定用 JDK 动态代理还是 CGLIB。
  4. 调用proxyFactory.getProxy(classLoader)生成代理对象。

代理方式的选择逻辑在ProxyConfig和ProxyProcessorSupport里。默认规则是:如果目标类实现了接口,用 JDK 动态代理;没实现接口,用 CGLIB。可以通过proxyTargetClass属性强制走 CGLIB。DefaultAdvisorAutoProxyCreator继承自ProxyProcessorSupport,所以它有setProxyTargetClass方法。

这里要提醒一个实操细节:DefaultAdvisorAutoProxyCreator默认的proxyTargetClass是false。如果你代理的 Bean 实现了接口,但你在业务代码里直接注入实现类而不是接口,会报类型转换异常,因为容器里放的是 JDK 代理对象,它只实现了接口,不是实现类的实例。解决办法要么改成注入接口,要么把proxyTargetClass设为true。

4. 手把手实操:从零搭一个 DefaultAdvisorAutoProxyCreator 生效的 Demo

4.1 环境与依赖准备

我用的是 Spring Framework 5.3.x 加 JDK 8,不引入 Spring Boot,这样能最纯粹地看到DefaultAdvisorAutoProxyCreator的行为。如果你用 Spring Boot,需要先排除掉自动装配的AnnotationAwareAspectJAutoProxyCreator,否则两个创建器会打架。

Maven 依赖只需要两个:

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.30</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.30</version> </dependency>

spring-aop里包含了DefaultAdvisorAutoProxyCreator和ProxyFactory相关类,spring-context提供容器。不需要 aspectjweaver,因为我们不走@Aspect那条路。

4.2 定义业务接口与实现

先写一个最普通的业务接口和实现,模拟真实场景:

public interface OrderService { String createOrder(String userId); String queryOrder(String orderId); } public class OrderServiceImpl implements OrderService { @Override public String createOrder(String userId) { System.out.println("执行创建订单,用户:" + userId); return "ORDER-001"; } @Override public String queryOrder(String orderId) { System.out.println("执行查询订单,订单号:" + orderId); return "订单详情"; } }

4.3 自定义一个 Advisor

关键来了,我们要手动定义一个 Advisor。最简单的方式是继承AbstractPointcutAdvisor或者直接用DefaultPointcutAdvisor配合自定义的MethodInterceptor。

public class LogMethodInterceptor implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { long start = System.currentTimeMillis(); try { return invocation.proceed(); } finally { long cost = System.currentTimeMillis() - start; System.out.println("[耗时统计] 方法:" + invocation.getMethod().getName() + ",耗时:" + cost + "ms"); } } }

然后定义一个切点,只匹配createOrder方法:

public class CreateOrderPointcut extends StaticMethodMatcherPointcut { @Override public boolean matches(Method method, Class<?> targetClass) { return "createOrder".equals(method.getName()); } }

组装成 Advisor:

@Configuration public class AopConfig { @Bean public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(false); return creator; } @Bean public DefaultPointcutAdvisor logAdvisor() { DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(); advisor.setPointcut(new CreateOrderPointcut()); advisor.setAdvice(new LogMethodInterceptor()); return advisor; } @Bean public OrderService orderService() { return new OrderServiceImpl(); } }

4.4 启动验证与结果解读

写个 main 方法启动容器:

public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class); OrderService orderService = ctx.getBean(OrderService.class); System.out.println("代理对象类型:" + orderService.getClass().getName()); orderService.createOrder("user-001"); orderService.queryOrder("ORDER-001"); } }

预期输出:

代理对象类型:com.sun.proxy.$Proxy12 执行创建订单,用户:user-001 [耗时统计] 方法:createOrder,耗时:0ms 执行查询订单,订单号:ORDER-001

可以看到createOrder被拦截了,queryOrder没有,因为切点只匹配前者。代理对象是$Proxy12,说明走的是 JDK 动态代理,因为OrderServiceImpl实现了接口。

如果你把creator.setProxyTargetClass(true)改成true,代理对象类型会变成OrderServiceImpl$$EnhancerBySpringCGLIB$$xxx,两个方法都会被 CGLIB 代理,但只有匹配切点的方法会走拦截器。

4.5 参数选择背后的逻辑

为什么proxyTargetClass默认是false?这是历史设计。JDK 动态代理基于接口,生成的代理类轻量、无字节码生成开销,是 Spring 早期推荐的方式。CGLIB 需要生成子类字节码,启动稍慢,但能代理没有接口的类。

实操建议:如果你的 Bean 都面向接口编程,保持false;如果存在直接注入实现类的情况,或者类没有接口,设成true。现代 Spring Boot 2.x 之后默认就是true了,因为 CGLIB 的性能已经足够好,而且能避免很多类型转换的坑。

5. 常见问题与排查技巧实录

5.1 AOP 不生效的排查顺序

遇到"AOP 没生效",按这个顺序查,基本能覆盖 90% 的情况:

  1. 确认自动代理创建器是否注册。打印容器里所有BeanPostProcessor类型的 Bean,看有没有DefaultAdvisorAutoProxyCreator或AnnotationAwareAspectJAutoProxyCreator。没有的话,代理根本不会发生。
  2. 确认 Advisor 是否在容器里。findCandidateAdvisors是从容器捞 Advisor 的,如果你的 Advisor 没注册成 Bean,或者注册了但类型不对,就捞不到。
  3. 确认切点是否匹配。把切点表达式单独拿出来,用AopUtils.canApply手动测一下目标类。
  4. 确认方法调用是否走代理。这是最隐蔽的坑——类内部方法自调用不走代理。比如createOrder内部调用this.queryOrder(),queryOrder上的切面不会生效,因为this是原始对象不是代理对象。
  5. 确认 Bean 是否被提前初始化。如果某个 Bean 在BeanPostProcessor注册之前就被创建了,它不会被代理。这种情况常见于@Bean方法里依赖了另一个 Bean,导致初始化顺序错乱。

5.2 动态添加 Advisor 不生效的问题

前面提到advisedBeans缓存,这里展开说。假设你的系统在运行时通过配置中心动态往容器注册新的 Advisor,期望之前没被代理的 Bean 现在被代理。但AbstractAutoProxyCreator的advisedBeans缓存里已经记录了"这个 Bean 不需要代理",后续不会再重新判断。

解决办法有两个:一是手动清除缓存,通过反射拿到advisedBeans字段清空;二是干脆不用DefaultAdvisorAutoProxyCreator,改用ProxyFactory手动重建代理。第一种方式侵入性强、依赖内部实现,不推荐;第二种更可控。

我个人的经验是:如果 Advisor 是动态的,最好在系统启动时就全部注册好,避免运行时变更。实在需要动态,考虑用BeanDefinitionRegistry动态注册 BeanDefinition 后刷新容器,但代价很大。

5.3 代理对象类型转换异常速查表

异常信息根本原因解决方案
$Proxy12 cannot be cast to XxxServiceImplJDK 代理只实现接口,注入实现类失败注入接口,或设proxyTargetClass=true
No qualifying bean of type XxxService代理对象类型与注入类型不匹配检查接口与实现类关系
BeanCurrentlyInCreationExceptionAdvisor 依赖了被代理的 Bean,循环依赖拆分 Advisor 依赖,或用@Lazy
IllegalStateException: Cannot apply...切点表达式语法错误检查表达式,用AopUtils验证
代理对象方法调用无拦截类内部自调用通过 AopContext 或注入自身代理

5.4 几个容易忽略的实操心得

心得一:基础设施类不要代理。AbstractAutoProxyCreator里有个isInfrastructureClass判断,Advisor、Advice、Pointcut这些类本身不会被代理。如果你自定义的类继承了这些,也会被跳过。这个设计是为了避免无限递归代理。

心得二:@Bean方法返回的 Bean 也会被代理。很多人以为只有@Component扫描的 Bean 才会被自动代理,其实@Bean方法注册的 Bean 同样会经过BeanPostProcessor,一样会被代理。区别只在于 Bean 的定义来源。

心得三:代理对象的equals和hashCode行为会变。JDK 代理对象的equals比较的是代理实例,不是目标对象。如果你在代码里用 Bean 作为 Map 的 key,代理前后行为可能不一致。这个坑很隐蔽,排查起来费劲。

心得四:多个自动代理创建器共存时,看 Ordered 顺序。AbstractAutoProxyCreator实现了Ordered,默认LOWEST_PRECEDENCE。如果两个创建器都匹配同一个 Bean,先执行的那个创建代理,后执行的看到已经是代理对象,wrapIfNecessary里会判断bean instanceof Advised直接返回。所以最终生效的是先执行的那个。想控制顺序就实现Ordered接口调setOrder。

6. 和 Spring Boot 自动装配的配合与冲突规避

6.1 Spring Boot 是怎么注册自动代理的

Spring Boot 的AopAutoConfiguration里,根据spring.aop.auto配置决定是否注册AnnotationAwareAspectJAutoProxyCreator。默认spring.aop.auto=true,所以 Boot 项目里默认就有自动代理能力,走的是@Aspect那条线。

spring.aop.proxy-target-class控制代理方式,Boot 2.x 默认true,即 CGLIB。这个配置最终会设置到AnnotationAwareAspectJAutoProxyCreator的proxyTargetClass属性上。

6.2 手动注册 DefaultAdvisorAutoProxyCreator 的正确姿势

如果你确实需要在 Boot 项目里用DefaultAdvisorAutoProxyCreator(比如你的 Advisor 是动态生成的,不走注解),正确做法是:

@Configuration public class CustomAopConfig { @Bean public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); creator.setOrder(Ordered.HIGHEST_PRECEDENCE); return creator; } }

注意几个点:方法用static修饰,避免配置类本身被过早初始化导致创建器注册时机不对;设置order让它优先于 Boot 默认的创建器执行;proxyTargetClass和 Boot 配置保持一致,避免行为不一致。

但更推荐的做法是:不要手动注册,而是把动态 Advisor 注册成 Bean,让 Boot 默认的AnnotationAwareAspectJAutoProxyCreator去处理。因为AnnotationAwareAspectJAutoProxyCreator继承自AbstractAdvisorAutoProxyCreator,它的findCandidateAdvisors同样会捞容器里所有 Advisor Bean,所以动态 Advisor 一样能被识别。这样既保留了@Aspect支持,又不用引入第二个创建器。

6.3 冲突排查的实操命令

怀疑两个创建器打架时,在启动类里加一段:

@Bean public CommandLineRunner checkAopCreators(ApplicationContext ctx) { return args -> { String[] names = ctx.getBeanNamesForType( AbstractAutoProxyCreator.class); for (String name : names) { Object bean = ctx.getBean(name); System.out.println("自动代理创建器:" + name + ",类型:" + bean.getClass().getName()); } }; }

如果打印出两个创建器,基本可以确认冲突。这时候要么去掉手动注册的那个,要么调整 order 明确优先级。

7. 性能与扩展:什么时候该用它,什么时候不该

7.1 性能开销分析

DefaultAdvisorAutoProxyCreator的性能开销主要在启动阶段:findCandidateAdvisors要实例化所有 Advisor Bean,findAdvisorsThatCanApply要对每个 Bean 做切点匹配。Bean 数量多、Advisor 数量多时,启动会明显变慢。

运行时的开销很小,因为匹配结果被advisedBeans缓存了,每次方法调用只是走一遍拦截器链。拦截器链本身是数组遍历,开销可以忽略。

优化建议:Advisor 数量控制在合理范围,避免注册几百个细粒度 Advisor。如果确实需要大量切点,考虑合并切点表达式,或者用@Aspect的复合切点。

7.2 扩展点:自定义 AbstractAdvisorAutoProxyCreator

如果你的场景比较特殊,比如 Advisor 不是从容器捞,而是从数据库或配置中心读,可以继承AbstractAdvisorAutoProxyCreator重写findCandidateAdvisors:

public class CustomAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator { @Override protected List<Advisor> findCandidateAdvisors() { List<Advisor> advisors = new ArrayList<>(); // 从配置中心读取规则,构建 Advisor advisors.addAll(loadFromConfigCenter()); return advisors; } }

这样既保留了自动代理的匹配和创建逻辑,又自定义了 Advisor 来源。这是框架二次封装时很常用的手法。

7.3 和 AspectJ 编译期织入的取舍

DefaultAdvisorAutoProxyCreator走的是运行时代理,只能拦截 Spring 容器管理的 Bean 的公开方法。如果你的需求是拦截 private 方法、构造方法、字段访问,或者拦截非 Spring 管理的对象,那运行时代理做不到,得用 AspectJ 编译期织入或加载期织入。

取舍逻辑很简单:Spring 容器内的 Bean 方法拦截,用自动代理足够;需要更细粒度的织入,才上 AspectJ。大部分业务场景用自动代理就够了,别为了炫技引入 AspectJ 增加构建复杂度。

8. 我踩过的几个真实坑

第一个坑是代理对象注入自身导致的循环依赖。有个 Service 里注入了自己,为了调用自己的另一个方法走代理。结果启动时报循环依赖。后来改成ApplicationContext.getBean延迟获取,或者用AopContext.currentProxy(),才解决。AopContext需要开启exposeProxy=true,这个配置在DefaultAdvisorAutoProxyCreator上通过setExposeProxy(true)设置。

第二个坑是切点匹配了 getter 方法导致序列化异常。有个切点表达式写得太宽,把getXxx方法也匹配了,结果 JSON 序列化时调用 getter 触发了切面逻辑,切面里又依赖了序列化上下文,死循环。排查了半天才发现是切点范围问题。教训是切点表达式一定要精确,别用execution(* com.xxx..*.*(..))这种大范围匹配。

第三个坑是CGLIB 代理 final 方法失效。有个类的方法被声明成final,CGLIB 无法重写,切面不生效。这个在 JDK 代理下不会出现,因为 JDK 代理基于接口。所以用 CGLIB 时要注意 final 方法和 final 类。

第四个坑是代理对象在@PostConstruct里调用自身方法。@PostConstruct执行时,Bean 已经初始化完成,理论上代理已经创建。但如果@PostConstruct里调用的是this.xxx(),走的还是原始对象,切面不生效。这个和类内部自调用是同一类问题。

这些坑的共同点是:代理的本质是"替换引用",任何绕过引用的调用都不会走代理。理解了这一点,大部分 AOP 不生效的问题都能自己想明白。

9. 一个可复用的排查清单

最后整理一份我平时排查 AOP 问题的清单,遇到问题按顺序过一遍:

  • 容器里有没有自动代理创建器?打印AbstractAutoProxyCreator类型的 Bean。
  • Advisor 有没有注册成 Bean?打印Advisor类型的 Bean。
  • 切点表达式能不能匹配目标类?用AopUtils.canApply验证。
  • 目标 Bean 是不是被提前初始化了?检查 Bean 初始化顺序。
  • 调用是不是走代理引用?排查类内部自调用。
  • 代理方式对不对?检查proxyTargetClass和接口实现情况。
  • 有没有 final 方法或 final 类?CGLIB 场景下要排除。
  • 有没有多个创建器冲突?检查 order 和注册来源。

这份清单覆盖了我这几年遇到的所有 AOP 代理问题。DefaultAdvisorAutoProxyCreator本身逻辑不复杂,复杂的是它和容器生命周期、其他创建器、业务代码调用方式的交互。把交互关系理清楚,源码读起来就顺了。

如果你正在做框架封装,我的建议是优先复用AnnotationAwareAspectJAutoProxyCreator,通过注册 Advisor Bean 的方式扩展,而不是另起炉灶注册DefaultAdvisorAutoProxyCreator。前者能兼容@Aspect,后者不能,而且两者共存容易出问题。只有在完全不使用注解切面、且需要完全自定义 Advisor 来源的场景下,才考虑单独用DefaultAdvisorAutoProxyCreator。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询