You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置AspectJ使Spring组件在@Around拦截前完成Autowired初始化

问题根因

该问题本质是Spring Bean初始化顺序和切面编织时机冲突:单元测试场景下若切面加载/编织时机过早,会提前触发目标Bean的实例化,此时@Autowired、@Value对应的依赖还未完成注入,就会触发空指针。

可行解决方案

1. 优先使用Spring AOP代理而非原生AspectJ编织

如果无需使用AspectJ专属特性,优先采用Spring自带的AOP实现,从根源避免初始化顺序问题:

  • 切面类同时标注@Aspect和@Component,交给Spring容器统一管理
  • 测试类/启动类添加@EnableAspectJAutoProxy注解,开启Spring AOP代理能力
    Spring AOP的代理对象会在所有Bean的属性注入、初始化流程完全完成后才生成,不会提前触发Bean实例化。

2. 若必须使用原生AspectJ编织,调整编织时机

如果需要用到AspectJ高级能力(比如拦截非public方法、非Spring管理类),可调整编织配置:

  • 单元测试阶段关闭编译时编织,改用加载时编织(LTW),在测试类上添加@EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
  • 切面类添加@Lazy注解,强制切面仅在第一次被调用时完成初始化,此时所有普通Bean已经完成依赖注入
  • 若使用Maven/Gradle的AspectJ编译插件,为测试阶段的编织规则添加过滤逻辑,仅编织完成初始化的Spring Bean实例

3. 优化切面执行逻辑,兼容初始化阶段

如果配置调整成本过高,可修改@Around切面的执行逻辑,跳过未完成初始化的Bean的拦截:

@Autowired
private ApplicationContext applicationContext;

@Around("execution(* com.your.business.targetMethod(..))")
public Object countExecTime(ProceedingJoinPoint pjp) throws Throwable {
    // 校验目标Bean是否处于初始化流程中
    String beanName = pjp.getTarget().getClass().getName();
    if (applicationContext.isSingletonCurrentlyInCreation(beanName)) {
        // 未完成初始化直接执行原方法
        return pjp.proceed();
    }
    // 正常执行计时逻辑
    long start = System.nanoTime();
    try {
        return pjp.proceed();
    } finally {
        long execTime = System.nanoTime() - start;
        // 自行处理计时结果输出/存储逻辑
    }
}

4. 单元测试专属优化

如果仅在单元测试场景出现该问题,可添加如下测试配置:

  • 测试类添加@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD),强制每个测试用例刷新独立的Spring上下文,避免上下文复用导致的初始化顺序异常
  • 为切面类添加@Order(Ordered.LOWEST_PRECEDENCE)注解,设置切面为最低优先级,确保所有Bean初始化相关的处理器执行完成后,切面逻辑才生效

内容的提问来源于stack exchange,提问作者user1767316

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 05:06:07