との差 そして 質問する

との差 そして 質問する

学習中ですが、と の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、とをに注入します。BCBCA

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とプロパティを自動接続します。cccA

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.xxxABCcom.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 定義はなくなり、注釈が使えるようになりました。

最後の例として、注釈付きクラス、を保持しAB以下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>動作を理解するのに必要なすべてであることを願っています。

おすすめ記事