透過限定符最佳化基於註解的自動裝配

當可以確定一個主(或非備用)候選時,@Primary@Fallback 是在使用型別自動裝配多個例項的有效方法。

當您需要更多地控制選擇過程時,可以使用 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>
1 帶有 main 限定符值的 bean 與帶有相同限定符值的建構函式引數進行自動裝配。
2 帶有 action 限定符值的 bean 與帶有相同限定符值的建構函式引數進行自動裝配。

對於回退匹配,bean 名稱被視為預設限定符值。因此,您可以定義 idmain 的 bean,而不是使用巢狀的限定符元素,從而產生相同的匹配結果。然而,儘管您可以使用此約定按名稱引用特定 bean,但 @Autowired 根本上是帶有可選語義限定符的型別驅動注入。這意味著限定符值,即使在 bean 名稱回退的情況下,也總是在型別匹配集合中具有縮小語義。它們在語義上不表達對唯一 bean id 的引用。好的限定符值是 mainEMEApersistent,它們表達了特定元件的特徵,獨立於 bean id,在匿名 bean 定義(如前一個示例中的)情況下,bean id 可能是自動生成的。

限定符也適用於型別化集合,如前所述——例如,對於 Set<MovieCatalog>。在這種情況下,所有匹配的 bean,根據宣告的限定符,都將作為集合注入。這意味著限定符不必是唯一的。相反,它們構成篩選條件。例如,您可以定義多個具有相同限定符值“action”的 MovieCatalog bean,所有這些 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 限定符值僅在那些型別選定的候選者中考慮(例如,將 account 限定符與標有相同限定符標籤的 bean 進行匹配)。

對於本身被定義為集合、Map 或陣列型別的 bean,@Resource 是一個很好的解決方案,它透過唯一名稱引用特定的集合或陣列 bean。也就是說,您也可以透過 Spring 的 @Autowired 型別匹配演算法來匹配集合、Map 和陣列型別,只要在 @Bean 返回型別簽名或集合繼承層次結構中保留了元素型別資訊。在這種情況下,您可以使用限定符值在相同型別的集合中進行選擇,如前一段所述。

@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/> 標籤的子元素,然後指定 typevalue 來匹配您的自定義限定符註解。型別與註解的完全限定類名進行匹配。或者,為了方便起見,如果不存在名稱衝突的風險,您可以使用短類名。以下示例演示了這兩種方法:

<?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
}

要自動裝配的欄位用自定義限定符註解,幷包含 genreformat 兩個屬性的值,如以下示例所示:

  • 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 定義應包含匹配的限定符值。此示例還演示了您可以使用 bean 元屬性而不是 <qualifier/> 元素。如果可用,<qualifier/> 元素及其屬性具有優先權,但如果不存在此類限定符,則自動裝配機制會回退到 <meta/> 標籤中提供的值,如以下示例中的最後兩個 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 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>
© . This site is unofficial and not affiliated with VMware.