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

@Primary および @Fallback は、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 コンパイラーフラグが必要です。6.2 以降、コンテナーは Bean 名の一致に対して高速ショートカット解決を適用し、パラメーター名が Bean 名と一致し、一致をオーバーライドする型、修飾子、プライマリ条件がない場合、完全な型一致アルゴリズムをバイパスします。パラメーター名をターゲットの Bean 名と一致させることをお勧めします。

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

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

@Autowired は、注入時に自己参照 (つまり、現在注入されている 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>