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

Spring Boot测试中@Autowired注入的MyService为null问题排查

Spring Boot测试中注入CGLIB代理导致字段为空的问题分析与解决

问题重现

你的测试类代码如下:

@ExtendWith(SpringExtension.class)
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApplication.class)
class MyApplicationTest {
   @Autowired MyService myService;
}

启动测试时,MyService的真实实例完成构造和@PostConstruct初始化(字段均非空),但测试代码中注入的却是MyService$$EnhancerBySpringCGLIB代理类,且代理类的所有字段为null。

核心原因

这是Spring自身AOP代理机制导致的,和Mockito无关:

  • 当MyService被Spring AOP增强(比如标注了@Transactional、匹配了自定义切面等),且类未实现任何接口时,Spring会默认生成CGLIB子类代理。
  • CGLIB代理的本质是创建目标类的子类,代理类自身的字段不会继承真实实例的字段值,所有字段默认初始化为null。@PostConstruct是针对真实实例执行的初始化逻辑,但测试类中注入的是代理对象,直接访问代理的字段自然会得到null。

解决方案

1. 通过方法访问字段(推荐)

Spring的CGLIB代理会拦截所有方法调用并转发到真实实例,因此改用getter方法访问字段即可获取正确值:

// 替换直接字段访问
String value = myService.getSomeField();

2. 注入接口类型

如果MyService实现了对应接口(如IMyService),将注入类型改为接口:

@Autowired
IMyService myService;

此时Spring会使用JDK动态代理,虽然同样不能直接访问字段,但面向接口编程更符合Spring设计规范,也能避免CGLIB代理的字段访问问题。

3. 获取真实目标实例

若必须直接访问字段,可借助Spring的AopUtils工具类获取代理背后的真实实例:

import org.springframework.aop.support.AopUtils;

// 在测试代码中获取真实实例
MyService realService = (MyService) AopUtils.getTargetObject(myService);
// 此时访问realService的字段会得到正确值

4. 调整AOP代理配置

如果项目允许,可强制Spring使用JDK动态代理,在application-test.properties中添加:

spring.aop.proxy-target-class=false

注意:该配置生效的前提是MyService必须实现至少一个接口,否则Spring仍会使用CGLIB代理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:03:17