Java Spring 延迟加载 - 加载一个 bean 加载该类的所有 @Lazy bean
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23125397/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring lazy loading - loading one bean loads all @Lazy beans of that class
提问by phanin
I've declared two beans of same class type. Initialized them to be @Lazy
. @Autowiring
one bean of them automatically initialized the other bean as well. I was surprised to see that behavior. Just curious to know more about the mechanism.
我已经声明了两个相同类类型的 bean。将它们初始化为@Lazy
. @Autowiring
其中一个 bean 也会自动初始化另一个 bean。我很惊讶地看到这种行为。只是想了解更多有关该机制的信息。
Code
代码
//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);
}
}
Output
输出
helloworld2, my.entp.spring.HelloWorld@3a9bba
helloworld1, my.entp.spring.HelloWorld@163f7a1 // why was helloworld1 initialized?
Autowired: my.entp.spring.HelloWorld@3a9bba
If you observe the output, you may notice that the helloworld1
bean is initialized when helloworld2
is @Autowired
.
如果你观察输出,你可能会注意到helloworld1
,当豆被初始化helloworld2
为@Autowired
。
I tested by removing @Autowired
and it produced expected results: initialized none of the beans.
我通过删除进行了测试@Autowired
,它产生了预期的结果:没有初始化任何豆。
采纳答案by Sotirios Delimanolis
When using @Autowired
directly, the injection method is byType
. In other words, the container sees
@Autowired
直接使用时,注入方式为byType
。换句话说,容器看到
@Autowired
private HelloWorld helloworld2;
and tries to find a bean of type HelloWorld
in the ApplicationContext
to inject.
并尝试HelloWorld
在ApplicationContext
要注入的类型中找到一个 bean 。
The process of resolving the bean to be injected consists of getting all candidate beans which consists of creating the beans. So the beans being @Lazy
doesn't change anything. They will still have to be created in order to be injected.
解析要注入的 bean 的过程包括获取所有候选 bean,这包括创建 bean。所以豆子@Lazy
不会改变任何东西。它们仍然必须被创建才能被注入。
To clarify M. Deinum'scomment on the question, you've given your beans names. For example,
为了澄清M. Deinum对这个问题的评论,您已经给出了您的豆类名称。例如,
@Bean(name="helloworld1")
When the injection process takes place, Spring will find all candidate beans that can be injected. If there is more than one, it will filter through them and try to find the best candidate. If it can't, it will throw exceptions. One of the steps for finding a better candidate is comparing the bean name with the name of the target field. Since yours match, the bean named helloworld2
will be chosen.
当注入过程发生时,Spring 会找到所有可以注入的候选 bean。如果有多个,它将过滤它们并尝试找到最佳候选者。如果不能,它会抛出异常。寻找更好候选者的步骤之一是将 bean 名称与目标字段的名称进行比较。由于您的匹配,helloworld2
将选择命名的 bean 。
By removing @Autowired
, the beans are never requested from the ApplicationContext
and therefore never initialized.
通过删除@Autowired
,永远不会从 请求bean ApplicationContext
,因此永远不会初始化。
回答by Shailendra
From Spring docs
来自 Spring文档
However, when a lazy-initialized bean is a dependency of a singleton bean that is not lazy-initialized, the ApplicationContext creates the lazy-initialized bean at startup, because it must satisfy the singleton's dependencies. The lazy-initialized bean is injected into a singleton bean elsewhere that is not lazy-initialized.
但是,当延迟初始化 bean 是未延迟初始化的单例 bean 的依赖项时,ApplicationContext 在启动时创建延迟初始化 bean,因为它必须满足单例的依赖项。延迟初始化的 bean 被注入到其他地方没有延迟初始化的单例 bean 中。
In your test case, the lazy bean is eagerly initialized as the default behaviour of Spring test facility is to prepare the test class instance fully (by injecting all dependencies eagerly) and then hand it over to JUnit. The exact place where it happens is DependencyInjectionTestExecutionListener
在您的测试用例中,惰性 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);
}
回答by Isaiah Alchemoth
Old post but still. Now you can do that:
旧帖子,但仍然。现在你可以这样做:
@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");
}
And/or @Qualifier:
和/或@Qualifier:
@Autowired
@Qualifier("helloworld2")
private HelloWorld hello;
回答by Neo
This seems to be a Spring bug at that time if helloworld1
bean was being initialized as well.
The @Lazy
annotation will play a role in deciding when the Spring beans get initialized irrespective of where it is being called from (tests or otherwise).
如果helloworld1
bean 也被初始化,这在当时似乎是一个 Spring 错误。该@Lazy
注释将在当Spring bean的获得,不论它被从(试验或其他方式)被称为初始化决定发挥作用。
Just tried this with Spring 5.1.0
and it correctly initializes only the helloworld2
bean.
刚刚用 Spring 尝试过这个,5.1.0
它只正确初始化了helloworld2
bean。
If you happen to remove the @Lazy
annotation, then both helloworld1
and helloworld2
beans will be initialized.
This is due to the fact that Spring will instantiate all eager beans as part of the refresh
phase. Once all the Spring beans are discovered in the invokeBeanFactoryPostProcessors
phase, Spring calls preInstantiateSingletons
for all the beans in the beanFactory
which will initialize all the eager beans (some Lazy beans too if they are needed as part of eager bean initialization).
如果您碰巧删除了@Lazy
注释,那么helloworld1
和helloworld2
bean 都将被初始化。这是因为 Spring 将实例化所有渴望的 bean 作为refresh
阶段的一部分。一旦在invokeBeanFactoryPostProcessors
阶段中发现了所有 Spring bean ,Spring 就会调用preInstantiateSingletons
中的所有 bean,beanFactory
这将初始化所有 Eager bean(如果作为 Eager bean 初始化的一部分需要,也可以使用一些 Lazy beans)。
If your case, since the top level bean SpringAppContext
is lazy, this applies to both the HelloWorld
type beans as well.
如果您的情况,由于顶级 beanSpringAppContext
是惰性的,这也适用于HelloWorld
类型 bean。
If you remove the @Autowired
, no beans are eagerly created nor needed for auto-wiring and hence no initialization.
如果删除@Autowired
,则不会急切地创建 bean,也不需要自动装配,因此无需初始化。