我已经声明了两个相同类型的bean。将它们初始化为@Lazy
。 @Autowiring
它们中的一个bean也自动初始化了另一个bean。我很惊讶地看到这种行为。只是想知道更多有关机制的信息。
代码
//bean
public class HelloWorld {
public HelloWorld(String msg){
System.out.println( msg + ", " + this);
}
}
@Configuration
@Lazy
public class SpringAppContext {
@Bean(name="helloworld1")
public HelloWorld helloworld1(){
return new HelloWorld("helloworld1");
}
@Bean(name="helloworld2")
public HelloWorld helloworld2(){
return new HelloWorld("helloworld2");
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={SpringAppContext.class})
public class SpringBeanLazyLoadTest {
@Autowired
private HelloWorld helloworld2;
@Test // this test is lame but just trying out.
public void print(){
System.out.println("Autowired: " + helloworld2);
}
}
输出
helloworld2, my.entp.spring.HelloWorld@3a9bba
helloworld1, my.entp.spring.HelloWorld@163f7a1 // why was helloworld1 initialized?
Autowired: my.entp.spring.HelloWorld@3a9bba
如果观察输出,您可能会注意到helloworld1
helloworld2
@Autowired
时@Autowired
bean已初始化。
我通过删除{{1}}进行了测试,并产生了预期结果:没有初始化bean。
答案 0 :(得分:9)
直接使用@Autowired
时,注入方法为byType
。换句话说,容器看到了
@Autowired
private HelloWorld helloworld2;
并尝试在HelloWorld
中找到要注入的ApplicationContext
类型的bean。
解析要注入的bean的过程包括获取包含创建bean的所有候选bean。因此,@Lazy
的bean不会改变任何东西。它们仍然必须被创建才能被注入。
为了澄清M. Deinum's对此问题的评论,您已经提供了豆类名称。例如,
@Bean(name="helloworld1")
当注射过程发生时,Spring会找到所有可注射的候选bean。如果有多个,它将过滤掉它们并尝试找到最佳候选者。如果它不能,它将抛出异常。找到更好的候选者的步骤之一是将bean名称与目标字段的名称进行比较。由于您的匹配,将选择名为helloworld2
的bean。
删除@Autowired
后,永远不会从ApplicationContext
请求bean,因此永远不会初始化。
答案 1 :(得分:2)
从春天docs
但是,当懒惰初始化的bean是单例的依赖项时 不是延迟初始化的bean,ApplicationContext创建 启动时懒惰初始化的bean,因为它必须满足 单身人士的依赖。懒惰初始化的bean被注入到 其他地方没有懒惰初始化的单例bean。
在你的测试用例中,lazy bean被急切地初始化,因为Spring测试工具的默认行为是完全准备测试类实例(通过急切地注入所有依赖项)然后将其交给JUnit。它发生的确切位置是DependencyInjectionTestExecutionListener
protected void injectDependencies(final TestContext testContext) throws Exception {
Object bean = testContext.getTestInstance();
AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext().getAutowireCapableBeanFactory();
beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
beanFactory.initializeBean(bean, testContext.getTestClass().getName());
testContext.removeAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE);
}
答案 2 :(得分:1)
老帖但仍然。现在你可以这样做:
@Bean(name="helloworld1", autowire=Autowire.BY_NAME)
public HelloWorld helloworld1(){
return new HelloWorld("helloworld1");
}
@Bean(name="helloworld2", autowire=Autowire.BY_NAME)
public HelloWorld helloworld2(){
return new HelloWorld("helloworld2");
}
和/或 @Qualifier :
@Autowired
@Qualifier("helloworld2")
private HelloWorld hello;
答案 3 :(得分:0)
如果helloworld1
bean也被初始化,那似乎是Spring的错误。
@Lazy
注释将在决定何时初始化Spring bean的过程中发挥作用,而与从何处调用它无关(测试或其他方式)。
只需使用Spring 5.1.0
进行尝试,它就可以正确地仅初始化helloworld2
bean。
如果碰巧删除了@Lazy
批注,则helloworld1
和helloworld2
bean都将被初始化。
这是由于Spring将在refresh
阶段中实例化所有渴望的bean。一旦在invokeBeanFactoryPostProcessors
阶段发现了所有Spring Bean,Spring就会为preInstantiateSingletons
中的所有bean调用beanFactory
,这将初始化所有渴望的bean(如果需要的话,还有一些Lazy bean作为热切的bean初始化的一部分)。
如果您的情况是这样,由于顶级bean SpringAppContext
是惰性的,因此这同样适用于HelloWorld
类型的bean。
如果删除@Autowired
,则不会急切创建bean,也不需要进行自动装配,因此无需初始化。