修飾子を使用したアノテーションベースのオートワイヤーの微調整

@Primary は、1 つの 1 次候補を決定できる場合に、複数のインスタンスで型ごとのオートワイヤーを使用する効果的な方法です。選択プロセスをさらに制御する必要がある場合は、Spring の @Qualifier アノテーションを使用できます。修飾子の値を特定の引数に関連付けて、特定の Bean が各引数に選択されるように型一致のセットを絞り込みます。最も単純なケースでは、次の例に示すように、これはわかりやすい説明的な値になります。

  • Java

  • Kotlin

public class MovieRecommender {

	@Autowired
	@Qualifier("main")
	private MovieCatalog movieCatalog;

	// ...
}
class MovieRecommender {

	@Autowired
	@Qualifier("main")
	private lateinit var movieCatalog: MovieCatalog

	// ...
}

次の例に示すように、個々のコンストラクター引数またはメソッドパラメーターに @Qualifier アノテーションを指定することもできます。

  • Java

  • Kotlin

public class MovieRecommender {

	private final MovieCatalog movieCatalog;

	private final CustomerPreferenceDao customerPreferenceDao;

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

	// ...
}
class MovieRecommender {

	private lateinit var movieCatalog: MovieCatalog

	private lateinit var customerPreferenceDao: CustomerPreferenceDao

	@Autowired
	fun prepare(@Qualifier("main") movieCatalog: MovieCatalog,
				customerPreferenceDao: CustomerPreferenceDao) {
		this.movieCatalog = movieCatalog
		this.customerPreferenceDao = customerPreferenceDao
	}

	// ...
}

次の例は、対応する Bean 定義を示しています。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		https://www.springframework.org/schema/context/spring-context.xsd">

	<context:annotation-config/>

	<bean class="example.SimpleMovieCatalog">
		<qualifier value="main"/> (1)

		<!-- inject any dependencies required by this bean -->
	</bean>

	<bean class="example.SimpleMovieCatalog">
		<qualifier value="action"/> (2)

		<!-- inject any dependencies required by this bean -->
	</bean>

	<bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>
1main 修飾子値を持つ Bean は、同じ値で修飾されたコンストラクター引数に関連付けられます。
2action 修飾子値を持つ Bean は、同じ値で修飾されたコンストラクター引数に関連付けられます。

フォールバック一致の場合、Bean 名はデフォルトの修飾子値と見なされます。ネストされた修飾子要素の代わりに main の id を使用して Bean を定義すると、同じ一致結果が得られます。ただし、この規則を使用して特定の Bean を名前で参照することはできますが、@Autowired は基本的に、オプションのセマンティック修飾子を使用した型駆動型注入に関するものです。これは、Bean 名のフォールバックがある場合でも、修飾子の値は、型一致のセット内で常に狭義のセマンティクスを持つことを意味します。それらは、一意の Bean id への参照を意味的に表現しません。適切な修飾子の値は main または EMEA または persistent で、Bean id から独立した特定のコンポーネントの特性を表します。これは、前述の例のような匿名 Bean 定義の場合に自動生成される場合があります。

前述のように、修飾子は型付きコレクションにも適用されます。たとえば、Set<MovieCatalog> に適用されます。この場合、宣言された修飾子に従って、一致するすべての Bean がコレクションとして注入されます。これは、修飾子が一意である必要がないことを意味します。むしろ、それらはフィルタリング条件を構成します。例: 同じ修飾子値「アクション」を持つ複数の MovieCatalog Bean を定義できます。これらはすべて、@Qualifier("action") アノテーションが付けられた Set<MovieCatalog> に注入されます。

型が一致する候補内で、修飾子の値がターゲット Bean 名に対して選択されるようにすると、インジェクションポイントで @Qualifier アノテーションは必要ありません。他の解決インジケーター (修飾子やプライマリマーカーなど) がない場合、非一意の依存関係の状況では、Spring はインジェクションポイント名 (つまり、フィールド名またはパラメーター名) をターゲット Bean 名と照合し、同じ名前の候補があればそれを選択します (Bean 名または関連付けられたエイリアスによる)。

バージョン 6.1 以降、これには -parameters Java コンパイラーフラグが存在する必要があります。

名前による注入の代替として、JSR-250 @Resource アノテーションを検討してください。これは、特定のターゲットコンポーネントをその一意の名前で識別するように意味的に定義されており、宣言された型はマッチングプロセスには関係ありません。@Autowired は、かなり異なる意味を持ちます。型によって候補 Bean を選択した後、指定された String 修飾子の値は、型選択された候補内でのみ考慮されます (たとえば、同じ修飾子ラベルでマークされた Bean に対して account 修飾子をマッチングします)。

それ自体がコレクション、Map、配列型として定義されている Bean の場合、@Resource は、特定のコレクションまたは配列 Bean を一意の名前で参照する優れたソリューションです。とはいえ、4.3 では、要素型情報が @Bean 戻り型シグネチャーまたはコレクション継承階層に保持されている限り、Spring の @Autowired 型マッチングアルゴリズムを介してコレクション、Map、配列型を照合できます。この場合、前の段落で概説したように、修飾子の値を使用して、同じ型のコレクションから選択できます。

4.3 以降、@Autowired は注入の自己参照(つまり、現在注入されている Bean への参照)も考慮します。自己注入はフォールバックであることに注意してください。他のコンポーネントへの定期的な依存関係は常に優先されます。その意味で、自己参照は通常の候補者選考には参加しないため、特に初心者になることはありません。それどころか、それらは常に最低の優先順位になります。実際には、自己参照は最後の手段としてのみ使用する必要があります(たとえば、Bean のトランザクションプロキシを介して同じインスタンスで他のメソッドを呼び出す場合など)。このようなシナリオでは、影響を受けるメソッドを別のデリゲート Bean に除外することを検討してください。または、@Resource を使用することもできます。これにより、一意の名前で現在の Bean にプロキシを戻すことができます。

同じ構成クラスの @Bean メソッドから結果を注入しようとすることも、事実上自己参照シナリオです。構成クラスのオートワイヤーフィールドとは対照的に、実際に必要なメソッドシグネチャーでそのような参照を遅延解決するか、影響を受ける @Bean メソッドを static として宣言し、含む構成クラスインスタンスとそのライフサイクルから切り離します。それ以外の場合、そのような Bean はフォールバックフェーズでのみ考慮され、他の構成クラスの一致する Bean が代わりにプライマリ候補として選択されます(利用可能な場合)。

@Autowired は、フィールド、コンストラクター、複数引数メソッドに適用され、パラメーターレベルで修飾子のアノテーションを絞り込むことができます。対照的に、@Resource は、単一の引数を持つフィールドおよび Bean プロパティ setter メソッドに対してのみサポートされます。結果として、注入ターゲットがコンストラクターまたは複数引数メソッドである場合、修飾子を使用する必要があります。

独自のカスタム修飾子アノテーションを作成できます。これを行うには、次の例に示すように、アノテーションを定義し、定義内で @Qualifier アノテーションを提供します。

  • Java

  • Kotlin

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {

	String value();
}
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class Genre(val value: String)

次に、次の例に示すように、オートワイヤーされたフィールドとパラメーターにカスタム修飾子を提供できます。

  • Java

  • Kotlin

public class MovieRecommender {

	@Autowired
	@Genre("Action")
	private MovieCatalog actionCatalog;

	private MovieCatalog comedyCatalog;

	@Autowired
	public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
		this.comedyCatalog = comedyCatalog;
	}

	// ...
}
class MovieRecommender {

	@Autowired
	@Genre("Action")
	private lateinit var actionCatalog: MovieCatalog

	private lateinit var comedyCatalog: MovieCatalog

	@Autowired
	fun setComedyCatalog(@Genre("Comedy") comedyCatalog: MovieCatalog) {
		this.comedyCatalog = comedyCatalog
	}

	// ...
}

次に、候補 Bean 定義の情報を提供できます。<qualifier/> タグを <bean/> タグのサブエレメントとして追加してから、type および value を指定して、カスタム修飾子アノテーションに一致させることができます。型は、アノテーションの完全修飾クラス名と照合されます。または、名前の競合のリスクが存在しない場合の便宜として、短いクラス名を使用できます。次の例は、両方のアプローチを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		https://www.springframework.org/schema/context/spring-context.xsd">

	<context:annotation-config/>

	<bean class="example.SimpleMovieCatalog">
		<qualifier type="Genre" value="Action"/>
		<!-- inject any dependencies required by this bean -->
	</bean>

	<bean class="example.SimpleMovieCatalog">
		<qualifier type="example.Genre" value="Comedy"/>
		<!-- inject any dependencies required by this bean -->
	</bean>

	<bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

クラスパススキャンと管理対象コンポーネントでは、XML で修飾子メタデータを提供する代わりのアノテーションベースの代替手段を見ることができます。具体的には、アノテーション付きの修飾子メタデータの提供を参照してください。

場合によっては、値なしでアノテーションを使用するだけで十分な場合があります。これは、アノテーションがより一般的な目的に役立ち、いくつかの異なる型の依存関係に適用できる場合に役立ちます。例: インターネットに接続できないときに検索できるオフラインカタログを提供できます。最初に、次の例に示すように、簡単なアノテーションを定義します。

  • Java

  • Kotlin

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
}
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class Offline

次に、次の例に示すように、オートワイヤーするフィールドまたはプロパティにアノテーションを追加します。

  • Java

  • Kotlin

public class MovieRecommender {

	@Autowired
	@Offline (1)
	private MovieCatalog offlineCatalog;

	// ...
}
1 この行は、@Offline アノテーションを追加します。
class MovieRecommender {

	@Autowired
	@Offline (1)
	private lateinit var offlineCatalog: MovieCatalog

	// ...
}
1 この行は、@Offline アノテーションを追加します。

これで、次の例に示すように、Bean 定義には修飾子 type のみが必要になります。

<bean class="example.SimpleMovieCatalog">
	<qualifier type="Offline"/> (1)
	<!-- inject any dependencies required by this bean -->
</bean>
1 この要素は修飾子を指定します。

単純な value 属性に加えて、またはその代わりに、名前付き属性を受け入れるカスタム修飾子アノテーションを定義することもできます。オートワイヤーされるフィールドまたはパラメーターに複数の属性値が指定されている場合、Bean 定義は、オートワイヤーの候補と見なされるすべての属性値と一致する必要があります。例として、次のアノテーション定義を検討してください。

  • Java

  • Kotlin

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {

	String genre();

	Format format();
}
@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class MovieQualifier(val genre: String, val format: Format)

この場合、Format は列挙型で、次のように定義されます。

  • Java

  • Kotlin

public enum Format {
	VHS, DVD, BLURAY
}
enum class Format {
	VHS, DVD, BLURAY
}

オートワイヤーされるフィールドには、カスタム修飾子でアノテーションが付けられ、次の例に示すように、両方の属性 genre および format の値が含まれます。

  • Java

  • Kotlin

public class MovieRecommender {

	@Autowired
	@MovieQualifier(format=Format.VHS, genre="Action")
	private MovieCatalog actionVhsCatalog;

	@Autowired
	@MovieQualifier(format=Format.VHS, genre="Comedy")
	private MovieCatalog comedyVhsCatalog;

	@Autowired
	@MovieQualifier(format=Format.DVD, genre="Action")
	private MovieCatalog actionDvdCatalog;

	@Autowired
	@MovieQualifier(format=Format.BLURAY, genre="Comedy")
	private MovieCatalog comedyBluRayCatalog;

	// ...
}
class MovieRecommender {

	@Autowired
	@MovieQualifier(format = Format.VHS, genre = "Action")
	private lateinit var actionVhsCatalog: MovieCatalog

	@Autowired
	@MovieQualifier(format = Format.VHS, genre = "Comedy")
	private lateinit var comedyVhsCatalog: MovieCatalog

	@Autowired
	@MovieQualifier(format = Format.DVD, genre = "Action")
	private lateinit var actionDvdCatalog: MovieCatalog

	@Autowired
	@MovieQualifier(format = Format.BLURAY, genre = "Comedy")
	private lateinit var comedyBluRayCatalog: MovieCatalog

	// ...
}

最後に、Bean 定義には一致する修飾子の値が含まれている必要があります。この例は、<qualifier/> 要素の代わりに Bean メタ属性を使用できることも示しています。可能な場合、<qualifier/> 要素とその属性が優先されますが、次の例の最後の 2 つの Bean 定義のように、そのような修飾子が存在しない場合、オートワイヤーメカニズムは <meta/> タグ内で提供される値にフォールバックします。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		https://www.springframework.org/schema/context/spring-context.xsd">

	<context:annotation-config/>

	<bean class="example.SimpleMovieCatalog">
		<qualifier type="MovieQualifier">
			<attribute key="format" value="VHS"/>
			<attribute key="genre" value="Action"/>
		</qualifier>
		<!-- inject any dependencies required by this bean -->
	</bean>

	<bean class="example.SimpleMovieCatalog">
		<qualifier type="MovieQualifier">
			<attribute key="format" value="VHS"/>
			<attribute key="genre" value="Comedy"/>
		</qualifier>
		<!-- inject any dependencies required by this bean -->
	</bean>

	<bean class="example.SimpleMovieCatalog">
		<meta key="format" value="DVD"/>
		<meta key="genre" value="Action"/>
		<!-- inject any dependencies required by this bean -->
	</bean>

	<bean class="example.SimpleMovieCatalog">
		<meta key="format" value="BLURAY"/>
		<meta key="genre" value="Comedy"/>
		<!-- inject any dependencies required by this bean -->
	</bean>

</beans>