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
相关产品推荐
相关产品推荐

