7

同じクラス タイプの 2 つの Bean を宣言しました。それらを に初期化しました@Lazy@Autowiringそれらの 1 つの 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

出力を観察すると、isのhelloworld1ときに Bean が初期化されていることがわかります。helloworld2@Autowired

削除@Autowiredしてテストしたところ、期待どおりの結果が得られました。どの Bean も初期化されませんでした。

4

4 に答える 4

10

@Autowired直接使用する場合の注入方法はbyTypeです。つまり、コンテナは

 @Autowired
 private HelloWorld helloworld2;

注入するタイプHelloWorldのBean を見つけようとします。ApplicationContext

注入される Bean を解決するプロセスは、Bean を作成することで構成されるすべての候補 Bean を取得することで構成されます。したがって、豆@Lazyは何も変わりません。注入するには、引き続き作成する必要があります。

質問に対するM. Deinum のコメントを明確にするために、Bean に名前を付けました。例えば、

 @Bean(name="helloworld1")

注入プロセスが行われると、Spring は注入できるすべての候補 Bean を見つけます。複数ある場合は、それらをフィルタリングして最適な候補を見つけようとします。できない場合は、例外がスローされます。より適切な候補を見つけるための手順の 1 つは、Bean 名をターゲット フィールドの名前と比較することです。あなたの名前が一致するので、名前helloworld2が付けられた Bean が選択されます。

を削除する@Autowiredと、Bean は から要求されApplicationContextなくなり、初期化されなくなります。

于 2014-04-17T05:53:06.967 に答える
2

春のドキュメントから

ただし、遅延初期化された Bean が遅延初期化されていないシングルトン Bean の依存関係である場合、ApplicationContext は起動時に遅延初期化された Bean を作成します。これは、シングルトンの依存関係を満たす必要があるためです。遅延初期化された Bean は、遅延初期化されていない別の場所にあるシングルトン Bean に注入されます。

あなたのテストケースでは、Spring テスト機能のデフォルトの動作は、(すべての依存関係を積極的に注入することによって) テストクラスインスタンスを完全に準備し、それを JUnit に渡すことであるため、レイジー Bean は積極的に初期化されます。それが起こる正確な場所は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

helloworld1Bean も初期化されていた場合、これは当時の Spring のバグのようです。アノテーションは@Lazy、Spring Bean が呼び出された場所 (テストまたはその他) に関係なく、Spring Bean がいつ初期化されるかを決定する役割を果たします。

Spring でこれを試してみたところ、 Bean5.1.0のみが正しく初期化されます。helloworld2

@Lazyアノテーションを削除すると、helloworld1helloworld2Bean の両方が初期化されます。これは、Spring がすべての熱心な Bean をrefreshフェーズの一部としてインスタンス化するためです。invokeBeanFactoryPostProcessorsフェーズですべての Spring Bean が検出されると、Spring は、すべての熱心な Bean を初期化preInstantiateSingletonsするすべての Bean を呼び出しbeanFactoryます (熱心な Bean の初期化の一部として必要な場合は、一部の Lazy Bean も)。

あなたの場合、最上位の Beanは遅延しているため、これは両方のタイプの Bean にSpringAppContextも当てはまります。HelloWorld

を削除する@Autowiredと、ビーンは積極的に作成されず、自動配線に必要とされないため、初期化されません。

于 2018-12-03T02:45:04.520 に答える
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 に答える