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です。