学習中ですが、と のSpring 3
背後にある機能についてよく理解できていないようです。<context:annotation-config>
<context:component-scan>
私が読んだところによると、それらは異なるアノテーション( @Required
、@Autowired
などと@Component
、@Repository
など)を処理するようです@Service
が、私が読んだところによると、それらは同じBean ポスト プロセッサクラスを登録します。
さらに混乱を招くのは、にannotation-config
属性<context:component-scan>
があることです。
これらのタグについて、誰か詳しく教えていただけますか? 何が似ていて、何が違いますか? どちらかが他方に取って代わられるのですか? 相互に補完し合うのですか? どちらか一方が必要なのでしょうか、それとも両方が必要なのでしょうか?
ベストアンサー1
<context:annotation-config>
アプリケーション コンテキストにすでに登録されている Bean 内のアノテーションをアクティブ化するために使用されます (XML で定義されたか、パッケージ スキャンによって定義されたかは関係ありません)。
<context:component-scan>
<context:annotation-config>
は、同じことを行うだけでなく、<context:component-scan>
パッケージをスキャンして、アプリケーション コンテキスト内で Bean を見つけて登録することもできます。
相違点と類似点を示すためにいくつかの例を挙げます。
まず、、 の 3 つの Bean の基本設定から始めA
、とをに注入します。B
C
B
C
A
package com.xxx;
public class B {
public B() {
System.out.println("creating bean B: " + this);
}
}
package com.xxx;
public class C {
public C() {
System.out.println("creating bean C: " + this);
}
}
package com.yyy;
import com.xxx.B;
import com.xxx.C;
public class A {
private B bbb;
private C ccc;
public A() {
System.out.println("creating bean A: " + this);
}
public void setBbb(B bbb) {
System.out.println("setting A.bbb with " + bbb);
this.bbb = bbb;
}
public void setCcc(C ccc) {
System.out.println("setting A.ccc with " + ccc);
this.ccc = ccc;
}
}
次の XML 構成の場合:
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A">
<property name="bbb" ref="bBean" />
<property name="ccc" ref="cBean" />
</bean>
コンテキストをロードすると、次の出力が生成されます。
creating bean B: com.xxx.B@c2ff5
creating bean C: com.xxx.C@1e8a1f6
creating bean A: com.yyy.A@1e152c5
setting A.bbb with com.xxx.B@c2ff5
setting A.ccc with com.xxx.C@1e8a1f6
これは期待通りの出力です。しかし、これは「古いスタイル」の Spring です。注釈ができたので、それを使用して XML を簡素化しましょう。
まず、次のように Bean の プロパティbbb
とプロパティを自動接続します。ccc
A
package com.yyy;
import org.springframework.beans.factory.annotation.Autowired;
import com.xxx.B;
import com.xxx.C;
public class A {
private B bbb;
private C ccc;
public A() {
System.out.println("creating bean A: " + this);
}
@Autowired
public void setBbb(B bbb) {
System.out.println("setting A.bbb with " + bbb);
this.bbb = bbb;
}
@Autowired
public void setCcc(C ccc) {
System.out.println("setting A.ccc with " + ccc);
this.ccc = ccc;
}
}
これにより、XML から次の行を削除できます。
<property name="bbb" ref="bBean" />
<property name="ccc" ref="cBean" />
私の XML は次のように簡略化されました。
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A" />
コンテキストをロードすると、次の出力が得られます。
creating bean B: com.xxx.B@5e5a50
creating bean C: com.xxx.C@54a328
creating bean A: com.yyy.A@a3d4cf
これは間違っています。何が起こったのでしょうか? プロパティが自動接続されないのはなぜですか?
そうですね、注釈は便利な機能ですが、それ自体では何もしません。注釈を付けるだけです。注釈を見つけてそれを使って何かを行うには、処理ツールが必要です。
<context:annotation-config>
助けになります。これにより、自身が定義されているのと同じアプリケーション コンテキストで定義されている Bean で見つかったアノテーションのアクションがアクティブ化されます。
XML を次のように変更します:
<context:annotation-config />
<bean id="bBean" class="com.xxx.B" />
<bean id="cBean" class="com.xxx.C" />
<bean id="aBean" class="com.yyy.A" />
アプリケーション コンテキストをロードすると、適切な結果が得られます。
creating bean B: com.xxx.B@15663a2
creating bean C: com.xxx.C@cd5f8b
creating bean A: com.yyy.A@157aa53
setting A.bbb with com.xxx.B@15663a2
setting A.ccc with com.xxx.C@cd5f8b
これはいいのですが、XML から 2 行削除して 1 行追加しました。これはそれほど大きな違いではありません。注釈の考え方は、XML を削除することです。
そこで、XML 定義を削除し、すべてを注釈に置き換えてみましょう。
package com.xxx;
import org.springframework.stereotype.Component;
@Component
public class B {
public B() {
System.out.println("creating bean B: " + this);
}
}
package com.xxx;
import org.springframework.stereotype.Component;
@Component
public class C {
public C() {
System.out.println("creating bean C: " + this);
}
}
package com.yyy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.xxx.B;
import com.xxx.C;
@Component
public class A {
private B bbb;
private C ccc;
public A() {
System.out.println("creating bean A: " + this);
}
@Autowired
public void setBbb(B bbb) {
System.out.println("setting A.bbb with " + bbb);
this.bbb = bbb;
}
@Autowired
public void setCcc(C ccc) {
System.out.println("setting A.ccc with " + ccc);
this.ccc = ccc;
}
}
XML では、次の内容のみを保持します。
<context:annotation-config />
コンテキストをロードすると、結果は... 何も起こりません。Bean は作成されず、Bean は自動接続されません。何も起こりません!
最初の段落で述べたように、 は<context:annotation-config />
アプリケーション コンテキスト内で登録された Bean に対してのみ機能するからです。3 つの Bean の XML 構成を削除したため、Bean は作成されず、<context:annotation-config />
機能する「ターゲット」もありません。
しかし、パッケージをスキャンして「ターゲット」を探すことができる場合は、これは問題になりません<context:component-scan>
。XML 構成の内容を次のエントリに変更してみましょう。
<context:component-scan base-package="com.xxx" />
コンテキストをロードすると、次の出力が得られます。
creating bean B: com.xxx.B@1be0f0a
creating bean C: com.xxx.C@80d1ff
うーん...何かが足りない。なぜ?
クラスをよく見ると、クラスにはA
パッケージがありますが、使用するパッケージcom.yyy
で指定したため、クラスは完全に見逃され、パッケージにあるものだけが取得されました。<context:component-scan>
com.xxx
A
B
C
com.xxx
これを修正するには、次の他のパッケージも追加します。
<context:component-scan base-package="com.xxx,com.yyy" />
そして期待通りの結果が得られました:
creating bean B: com.xxx.B@cd5f8b
creating bean C: com.xxx.C@15ac3c9
creating bean A: com.yyy.A@ec4a87
setting A.bbb with com.xxx.B@cd5f8b
setting A.ccc with com.xxx.C@15ac3c9
以上です。これで XML 定義はなくなり、注釈が使えるようになりました。
最後の例として、注釈付きクラス、を保持しA
、B
以下C
を XML に追加すると、コンテキストをロードした後に何が得られるでしょうか?
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
それでも正しい結果が得られます:
creating bean B: com.xxx.B@157aa53
creating bean C: com.xxx.C@ec4a87
creating bean A: com.yyy.A@1d64c37
setting A.bbb with com.xxx.B@157aa53
setting A.ccc with com.xxx.C@ec4a87
クラスの Bean がスキャンによって取得されない場合でも、 XML に手動で登録されたものであっても、アプリケーション コンテキストに登録されているすべての Bean にA
処理ツールが適用されます。<context:component-scan>
A
しかし、次の XML があった場合、<context:annotation-config />
と の両方を指定したため、重複した Bean が取得されるでしょうか<context:component-scan>
?
<context:annotation-config />
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
いいえ、重複はありません。期待どおりの結果が得られました。
creating bean B: com.xxx.B@157aa53
creating bean C: com.xxx.C@ec4a87
creating bean A: com.yyy.A@1d64c37
setting A.bbb with com.xxx.B@157aa53
setting A.ccc with com.xxx.C@ec4a87
これは、両方のタグが同じ処理ツール (が指定されている<context:annotation-config />
場合は省略可能<context:component-scan>
) を登録しますが、Spring はそれらを 1 回だけ実行するためです。
処理ツールを自分で複数回登録した場合でも、Spring は処理ツールが一度だけ実行されるようにします。次の XML を参照してください。
<context:annotation-config />
<context:component-scan base-package="com.xxx" />
<bean id="aBean" class="com.yyy.A" />
<bean id="bla" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla1" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla2" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<bean id="bla3" class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
それでも次の結果が生成されます。
creating bean B: com.xxx.B@157aa53
creating bean C: com.xxx.C@ec4a87
creating bean A: com.yyy.A@25d2b2
setting A.bbb with com.xxx.B@157aa53
setting A.ccc with com.xxx.C@ec4a87
はい、これで終わりです。
この情報と @Tomasz Nurkiewicz および @Sean Patrick Floyd からの回答が、仕組み<context:annotation-config>
と<context:component-scan>
動作を理解するのに必要なすべてであることを願っています。