5

SpringのIoCコンテナ機能とコンテキスト設定についての基本的な理解不足でしか説明できない問題に遭遇したので、これについて説明を求めたいと思います。

参考までに、私が保守しているアプリケーションには、次のテクノロジーのスタックがあります。

  • Java 1.6
  • 春2.5.6
  • RichFaces 3.3.1-GA UI
  • Springフレームワークは、DAOサポートに使用されるSpringJDBCモジュールを使用したBean管理に使用されます
  • Mavenはビルドマネージャーとして使用されます
  • JUnit4.4がテストエンジンとして導入されました

私は過去にさかのぼって(原文のまま)アプリケーションのJUnitテストを作成していますが、驚いたのは、@Autowire表記を使用せずにセッターインジェクションを使用してBeanをテストクラスにインジェクトでき​​なかったことです。

セットアップ例とそれに付随する構成ファイルを提供します。

テストクラスTypeTestは本当に簡単です:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest {

    @Autowired
    private IType type;

    @Test
    public void testFindAllTypes() {
        List<Type> result;

        try {
            result = type.findAlltTypes();
            assertNotNull(result);
        } catch (Exception e) {
            e.printStackTrace();
            fail("Exception caught with " + e.getMessage());
        }
    }
}

そのコンテキストは次のように定義されていTestStackOverflowExample-context.xmlます。

<context:property-placeholder location="classpath:testContext.properties" />
<context:annotation-config />
<tx:annotation-driven />

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">
    <property name="driverClassName" value="${db.connection.driver.class}" />
    <property name="url" value="${db.connection.url}" />
    <property name="username" value="${db.connection.username}" />
    <property name="password" value="${db.connection.password}" />
</bean>

<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<bean id="beanDAO" class="com.example.BeanDAOImpl">
    <property name="ds" ref="dataSource"></property>
    <property name="beanDAOTwo" ref="beanDAOTwo"></property>
</bean>

<bean id="beanDAOTwo" class="com.example.BeanDAOTwoImpl">
    <property name="ds" ref="dataSource"></property>
</bean>

<bean id="type" class="com.example.TypeImpl">
    <property name="beanDAO" ref="beanDAO"></property>
</bean>

TestContext.propertiesクラスパスにあり、データソースに必要なデータベース固有のデータのみが含まれています。

これは魅力のように機能しますが、私の質問は、次のように手動でBeanをワイヤリングしてセッターインジェクションを実行しようとすると、なぜ機能しないのかということです。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest {

    private IType type;

    public IType getType () {
        return type;
    }

    public void setType(IType type) {
        this.type= type;
    }

    @Test
    public void testFindAllTypes(){
    //snip, snip...
    }
}

ここで何が欠けていますか?ここで構成のどの部分が間違っていますか?セッターを介して手動でBeanを注入しようとすると、この部分が原因でテストが失敗します

result = type.findAlltTypes();

実行時にnullとして解決されます。もちろん、私はSpringリファレンスマニュアルを参照して、XML構成のさまざまな組み合わせを試しました。私が結論付けることができるのは、SpringがSpring Test Context参照を適切に逆参照できないため、SpringがBeanを注入できなかったということだけですが、@ Autowiredを使用すると、これは「自動的に」発生します。これは、Autowiredアノテーションとその両方のJavaDocが原因であるためです。PostProcessorクラスはこれについて言及していません。

@Autowiredまた、ここでのみアプリケーションで使用されるという事実も追加する価値があります。他の場所では手動配線のみが実行されるので、これも疑問を引き起こします-私のテストでは、なぜここでは機能せず、ここで機能しないのですか?DI構成のどの部分が欠落していますか?Spring Contextの参照をどのように取得しますか?@Autowired

編集:私もこれを試しましたが、同じ結果になりました:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest implements ApplicationContextAware{

    private IType type;

    private ApplicationContext ctx;

    public TypeTest(){
              super();
              ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
              ctx.getBean("type");
    }

    public IType getType () {
        return type;
    }

    public void setType(IType type) {
        this.type= type;
    }

    @Test
    public void testFindAllTypes(){
    //snip, snip...
    }
}

他に何かアイデアはありますか?

EDIT2:私は自分自身TestContextListenerやを書くことに頼ることなく方法を見つけましたBeanPostProcessor。それは驚くほど単純で、最後の編集で正しい方向に進んでいたことがわかりました。

1)コンストラクターベースのコンテキスト解決:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest{

    private IType type;

    private ApplicationContext ctx;

    public TypeTest(){
         super();
         ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
         type = ctx.getBean("type");
    }

    public IType getType () {
        return type;
    }

    public void setType(IType type) {
        this.type= type;
    }

    @Test
    public void testFindAllTypes(){
    //snip, snip...
    }
}

2)ApplicationContextAwareインターフェースを実装することにより:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest implements ApplicationContextAware{

    private IType type;
    private ApplicationContext ctx;

    public IType getType () {
        return type;
    }

    public void setType(IType type) {
        this.type= type;
    }

@Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
    this.ctx = ctx;
    type = (Type) ctx.getBean("type");
}

    @Test
    public void testFindAllTypes(){
    //snip, snip...
    }
}

これらのアプローチは両方とも、適切にインスタンス化されたBeanです。

4

1 に答える 1

5

のソースをorg.springframework.test.context.support.DependencyInjectionTestExecutionListener見ると、次の方法がわかります(わかりやすくするためにフォーマットおよびコメント化されています)。

protected void injectDependencies(final TestContext testContext)
throws Exception {
    Object bean = testContext.getTestInstance();
    AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext()
            .getAutowireCapableBeanFactory();
    beanFactory.autowireBeanProperties(bean, 

            AutowireCapableBeanFactory.AUTOWIRE_NO,
            // no autowiring!!!!!!!!

            false
        );

    beanFactory.initializeBean(bean, testContext.getTestClass().getName());
    // but here, bean post processors are run

    testContext.removeAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE);
}

したがって、テストオブジェクトは自動配線のないBeanです。ただし、@AutoWiredなど@Resourceは自動配線メカニズムを使用せず、を使用しますBeanPostProcessor。したがって、依存関係は、注釈が使用されている場合(またはそれを行う他の何かを登録した場合)にのみ注入されますBeanPostProcessor

(The above code is from Spring 3.0.x, but I bet it was the same in 2.5.x)

于 2010-11-09T12:46:22.357 に答える