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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 20:53:27  来源:igfitidea点击:

Spring lazy loading - loading one bean loads all @Lazy beans of that class

javaspringspring-bean

提问by phanin

I've declared two beans of same class type. Initialized them to be @Lazy. @Autowiringone 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 helloworld1bean is initialized when helloworld2is @Autowired.

如果你观察输出,你可能会注意到helloworld1,当豆被初始化helloworld2@Autowired

I tested by removing @Autowiredand it produced expected results: initialized none of the beans.

我通过删除进行了测试@Autowired,它产生了预期的结果:没有初始化任何豆。

采纳答案by Sotirios Delimanolis

When using @Autowireddirectly, 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 HelloWorldin the ApplicationContextto inject.

并尝试HelloWorldApplicationContext要注入的类型中找到一个 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 @Lazydoesn'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 helloworld2will be chosen.

当注入过程发生时,Spring 会找到所有可以注入的候选 bean。如果有多个,它将​​过滤它们并尝试找到最佳候选者。如果不能,它会抛出异常。寻找更好候选者的步骤之一是将 bean 名称与目标字段的名称进行比较。由于您的匹配,helloworld2将选择命名的 bean 。

By removing @Autowired, the beans are never requested from the ApplicationContextand 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 helloworld1bean was being initialized as well. The @Lazyannotation will play a role in deciding when the Spring beans get initialized irrespective of where it is being called from (tests or otherwise).

如果helloworld1bean 也被初始化,这在当时似乎是一个 Spring 错误。该@Lazy注释将在当Spring bean的获得,不论它被从(试验或其他方式)被称为初始化决定发挥作用。

Just tried this with Spring 5.1.0and it correctly initializes only the helloworld2bean.

刚刚用 Spring 尝试过这个,5.1.0它只正确初始化了helloworld2bean。

If you happen to remove the @Lazyannotation, then both helloworld1and helloworld2beans will be initialized. This is due to the fact that Spring will instantiate all eager beans as part of the refreshphase. Once all the Spring beans are discovered in the invokeBeanFactoryPostProcessorsphase, Spring calls preInstantiateSingletonsfor all the beans in the beanFactorywhich will initialize all the eager beans (some Lazy beans too if they are needed as part of eager bean initialization).

如果您碰巧删除了@Lazy注释,那么helloworld1helloworld2bean 都将被初始化。这是因为 Spring 将实例化所有渴望的 bean 作为refresh阶段的一部分。一旦在invokeBeanFactoryPostProcessors阶段中发现了所有 Spring bean ,Spring 就会调用preInstantiateSingletons中的所有 bean,beanFactory这将初始化所有 Eager bean(如果作为 Eager bean 初始化的一部分需要,也可以使用一些 Lazy beans)。

If your case, since the top level bean SpringAppContextis lazy, this applies to both the HelloWorldtype 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,也不需要自动装配,因此无需初始化。