Spring Boot:如何正确测试@Lazy注解Bean的懒加载行为?
@Lazy注解的Bean注册与懒加载测试问题
场景复现
配置类中通过@Bean+@Lazy定义了依赖DependencyBean的SomeBean:
@Configuration public class LazyConfig { @Bean @Lazy public SomeBean someBean(final DependencyBean dependencyBean) { return new SomeBean(dependencyBean); } }
使用ApplicationContextRunner编写测试,期望容器未加载SomeBean,但测试失败:
class LazyConfigTest { ApplicationContextRunner runner = new ApplicationContextRunner() .withUserConfiguration(LazyConfig.class); @Test void testNoLazyIsLoaded() { runner.run(context -> { org.assertj.core.api.Assertions.assertThat(context).getBeanNames(SomeBean.class).isEmpty(); // > Expecting empty but was: ["someBean"] org.assertj.core.api.Assertions.assertThat(context).doesNotHaveBean(SomeBean.class); // > Expecting not to have, but found: <["someBean"]> }); } }
尝试主动获取该Bean时,因依赖缺失报错:
context.getBean(SomeBean.class); // > Error creating bean with name 'someBean' defined in org.lucasvc.lazy.config.LazyConfig: Unsatisfied dependency expressed through method 'someBean' parameter 0: No qualifying bean of type 'org.lucasvc.lazy.DependencyBean' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
疑问
- 加了
@Lazy注解的Bean也会被注册到上下文吗? - 如何正确测试
@Lazy的懒加载行为?
解答
1. @Lazy不会阻止Bean注册到上下文
@Lazy注解的作用是延迟Bean的实例化,而非阻止Bean定义的注册。Spring容器启动时,会正常解析@Bean注解,将SomeBean的定义加载到上下文当中,所以getBeanNames(SomeBean.class)能查询到对应的Bean名称,doesNotHaveBean断言失败也是因为上下文已存在该Bean的注册信息。
此时Bean只是未被实例化,它的定义是存在的;只有当第一次尝试获取该Bean(比如调用getBean(SomeBean.class))时,Spring才会触发实例化流程,此时才会检查依赖是否存在——这也是主动获取Bean时出现依赖缺失报错的原因。
2. 正确测试@Lazy的懒加载行为
验证懒加载的核心是检查Bean是否尚未被实例化,而非检查Bean定义是否存在。可以通过以下两种方式实现:
方式一:验证Bean的懒加载配置与实例状态
通过获取BeanFactory,检查Bean定义的懒加载属性,同时验证Bean是否未被实例化为单例对象:
@Test void testLazyBeanNotInstantiated() { runner.run(context -> { DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory(); // 验证该Bean确实配置了懒加载 BeanDefinition beanDefinition = beanFactory.getBeanDefinition("someBean"); assertThat(beanDefinition.isLazyInit()).isTrue(); // 验证Bean尚未被实例化(单例池不存在该Bean) assertThat(beanFactory.containsSingleton("someBean")).isFalse(); }); }
方式二:验证实例化触发时机
先确认Bean未实例化,然后主动触发实例化(需确保依赖存在,比如临时注册DependencyBean),再验证实例已存在:
@Test void testLazyBeanInstantiatedOnDemand() { runner.withBean(DependencyBean.class) // 注册依赖Bean .run(context -> { DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory(); // 初始状态下Bean未实例化 assertThat(beanFactory.containsSingleton("someBean")).isFalse(); // 主动获取Bean,触发实例化 SomeBean someBean = context.getBean(SomeBean.class); // 验证Bean已实例化 assertThat(beanFactory.containsSingleton("someBean")).isTrue(); assertThat(someBean).isNotNull(); }); }
内容的提问来源于stack exchange,提问作者lucasvc
相关产品推荐
相关产品推荐

