7

我已经声明了两个相同类类型的 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

如果您观察输出,您可能会注意到helloworld1bean 在 is 时被helloworld2初始化@Autowired

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

4

4 回答 4

10

直接使用时@Autowired,注入方式为byType. 换句话说,容器看到

 @Autowired
 private HelloWorld helloworld2;

并尝试HelloWorldApplicationContext要注入的类型中找到一个 bean。

解析要注入的 bean 的过程包括获取所有候选 bean,其中包括创建 bean。所以豆子@Lazy不会改变任何东西。它们仍然必须被创建才能被注入。

为了澄清M. Deinum对这个问题的评论,你给了你的豆子的名字。例如,

 @Bean(name="helloworld1")

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

通过删除@Autowired,永远不会从 请求 bean ApplicationContext,因此永远不会初始化。

于 2014-04-17T05:53:06.967 回答
2

来自 Spring文档

但是,当延迟初始化的 bean 是未延迟初始化的单例 bean 的依赖项时,ApplicationContext 会在启动时创建延迟初始化的 bean,因为它必须满足单例的依赖项。延迟初始化的 bean 被注入到没有延迟初始化的其他地方的单例 bean 中。

在您的测试用例中,惰性 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);
    }
于 2014-04-17T05:48:26.517 回答
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;
于 2017-01-16T21:59:53.647 回答
1

helloworld1如果bean 也被初始化,这似乎是当时的 Spring 错误。注释将@Lazy在决定 Spring bean 何时初始化方面发挥作用,而不管它是从哪里调用的(测试或其他方式)。

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

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

如果您的情况是,由于顶级 beanSpringAppContext是惰性的,因此这也适用于两种HelloWorld类型的 bean。

如果删除@Autowired,则不会急切地创建 bean,也不需要自动装配,因此也不会初始化。

于 2018-12-03T02:45:04.520 回答