web-dev-qa-db-ja.com

Spring @Autowiredの使い方を理解する

Spring Autowiredアノテーションを理解するためにspring 3.0.xリファレンスドキュメントを読んでいます。

3.9.2 @Autowiredと@Inject

以下の例が理解できません。 XMLを機能させるには、XMLで何かする必要がありますか?

実施例1

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

実施例2

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
                    CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

どうやって2つのクラスが同じインターフェースを実装し、同じクラスを使用して自動生成されるのでしょうか。

例:

class Red implements Color
class Blue implements Color

class myMainClass{
    @Autowired 
    private Color color;

    draw(){
        color.design(); 
    } 
}

どの設計方法が呼ばれるのでしょうか? RedクラスのデザインメソッドがBlueではなく呼び出されるようにするにはどうすればよいですか。

270
NewQueries

TL; DR

@Autowiredアノテーションを使用すると、XMLファイル内で自分自身で配線を行う必要がなくなり、どこに注入する必要があるのか​​がわかります。

詳しい説明

@Autowiredアノテーションを使うと、何を注入しても構わないので他の場所で設定をスキップすることができます。あなたのパッケージがcom.mycompany.moviesであると仮定すると、あなたはあなたのXML(アプリケーションコンテキストファイル)にこのタグを入れなければなりません:

<context:component-scan base-package="com.mycompany.movies" />

このタグは自動スキャンを行います。 Beanになる必要がある各クラスに@Component(単純なBeanの場合)、@Controller(サーブレットコントロールの場合)、または@RepositoryDAOクラスの場合)などの正しいアノテーションが付けられ、これらのクラスがパッケージcom.mycompany.moviesの下にあると仮定します。これらのすべてとそれぞれのBeanを作成します。これは、2回のクラスのスキャンで行われます。最初に、Beanになる必要があるクラスを検索して実行する必要がある注入をマップし、2回目のスキャンではBeanを注入します。もちろん、より伝統的なXMLファイルで、または @Configuration クラス(または3つの任意の組み合わせ)でBeanを定義できます。

@Autowiredアノテーションは、インジェクションが発生する必要がある場所をSpringに伝えます。それをメソッドsetMovieFinderに置くと、(プレフィックスset + @Autowiredアノテーションによって)Beanをインジェクトする必要があることがわかります。 2回目のスキャンで、SpringはタイプMovieFinderのBeanを検索し、そのようなBeanが見つかった場合はそれをこのメソッドに注入します。そのようなBeanが2つ見つかった場合はExceptionが返されます。 Exceptionを回避するには、@Qualifierアノテーションを使用して、以下の方法で2つのBeanのうちどちらをインジェクトするかを指定できます。

@Qualifier("redBean")
class Red implements Color {
   // Class code here
}

@Qualifier("blueBean")
class Blue implements Color {
   // Class code here
}

XMLでBeanを宣言したい場合は、次のようになります。

<bean id="redBean" class="com.mycompany.movies.Red"/>

<bean id="blueBean" class="com.mycompany.movies.Blue"/>

@Autowired宣言では、2つのカラーBeanのどちらを注入するかを指示するために、@Qualifierも追加する必要があります。

@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
  this.color = color;
}

2つのアノテーション(@Autowired@Qualifier)を使用したくない場合は、@Resourceを使用してこれら2つを組み合わせることができます。

@Resource(name="redBean")
public void setColor(Color color) {
  this.color = color;
}

@Resource(この回答の最初のコメントでそれに関するいくつかの追加のデータを読むことができます)はあなたに2つのアノテーションの使用を免れる代わりにあなたは1つだけを使用します。

もう2つコメントを加えます。

  1. Spring固有ではなく、 @Inject標準の一部 であるため、@Autowiredの代わりにJSR-330を使用することをお勧めします。
  2. もう1つの良い方法は、メソッドではなくコンストラクタに@Inject/@Autowiredを設定することです。それをコンストラクターに置くと、注入されたBeanがnullではないことを検証し、アプリケーションを開始しようとすると失敗し、実際にBeanを使用する必要がある場合はNullPointerExceptionを回避できます。

更新 :図を完成させるために、私は@Configurationクラスについて new question を作成しました。

504
Avi

例には、「同じインターフェースを実装するクラス」ということは何もありません。 MovieCatalogは型で、CustomerPreferenceDaoは別の型です。春は彼らを簡単に区別することができます。

Spring 2.xでは、Beanの配線は主にBean IDまたは名前を介して行われていました。これはまだSpring 3.xでもサポートされていますが、多くの場合、特定のタイプのBeanのインスタンスが1つあります - ほとんどのサービスはシングルトンです。それらの名前を作成するのは面倒です。そこでSpringは「タイプによる自動配線」をサポートし始めました。

例が示すものは、フィールド、メソッド、およびコンストラクタにBeanを挿入するために使用できるさまざまな方法です。

各Beanで完全修飾クラス名を指定する必要があるため、XMLにはSpringが必要とするすべての情報がすでに含まれています。ただし、インターフェイスには少し注意が必要です。

この自動配線は失敗します。

 @Autowired
 public void prepare( Interface1 bean1, Interface1 bean2 ) { ... }

Javaはパラメータ名をバイトコードに保存しないため、Springは2つのBeanを区別できなくなります。修正方法は@Qualifierを使用することです。

 @Autowired
 public void prepare( @Qualifier("bean1") Interface1 bean1,
     @Qualifier("bean2")  Interface1 bean2 ) { ... }
17
Aaron Digulla

はい、Springサーブレットコンテキストxmlファイルを設定してBean(つまりクラス)を定義し、自動インジェクションを実行できるようにすることができます。ただし、Springを起動して実行するには他の設定を行う必要があります。そのための最善の方法は、チュートリアルを最初から実行することです。

Springの設定が完了したら、上記の例1のSpringサーブレットコンテキストxmlファイルで次の操作を実行できます( replace com.movi​​esのパッケージ名 trueにしてください)パッケージ名がで、これがサードパーティのクラスである場合は、適切なjarファイルがクラスパスにあることを確認してください。

<beans:bean id="movieFinder" class="com.movies.MovieFinder" />

あるいは、MovieFinderクラスにプリミティブ値を持つコンストラクターがある場合は、次のようになります。

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg value="100" />
</beans:bean>

あるいは、MovieFinderクラスに別のクラスを期待するコンストラクタがある場合は、次のようにします。

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg ref="otherBeanRef" />
</beans:bean>

... ' otherBeanRef 'は、期待されるクラスへの参照を持つ別のBeanです。

5
Cem Sultan