qualifier限定符

52 阅读6分钟

前言

@Primary @Fallback当可以确定一个主要(或非后备)候选者时,使用按类型自动装配多个实例的有效方法。

@Qualifier

当您需要更好地控制选择过程时,可以使用 Spring 的@Qualifier 注解。您可以将限定符值与特定参数关联,从而缩小类型匹配的范围,以便为每个参数选择特定的 Bean。在最简单的情况下,这可以是一个简单的描述性值,如下例所示:

public class MovieRecommender {

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

	// ...
}

您还可以@Qualifier在单个构造函数参数或方法参数上指定注解,如下例所示:

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

	// ...
}

下面的示例显示了相应的 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"/><!-- inject any dependencies required by this bean -->
	</bean>

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

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

</beans>

①具有限定符值的 Beanmain与具有相同值限定的构造函数参数连接。

②具有限定符值的 Beanaction与具有相同值限定的构造函数参数连接。

对于后备匹配,bean 名称被视为默认限定符值。因此,您可以使用 ofidmain不是嵌套的限定符元素来定义 bean,从而获得相同的匹配结果。但是,尽管您可以使用此约定按名称引用特定的 bean,但@Autowired本质上是关于使用可选语义限定符的类型驱动注入。这意味着,即使使用 bean 名称后备,限定符值在类型匹配集内也始终具有缩小语义。它们在语义上不表达对唯一 bean 的引用id。好的限定符值是main 或EMEApersistent,表达独立于 bean 的特定组件的特征id,如果是匿名 bean 定义(例如上例中的示例),则可以自动生成这些特征。

限定符也适用于类型集合,如前所述,例如, Set<MovieCatalog>。在这种情况下,所有匹配的 bean(根据声明的限定符)都将作为集合注入。这意味着限定符不必唯一。相反,它们构成了过滤条件。例如,您可以定义多个MovieCatalog具有相同限定符值“action”的 bean,所有这些 bean 都会被注入到Set<MovieCatalog>带有 的注解中@Qualifier("action")

在类型匹配的候选对象中,让限定符值根据目标 Bean 名称进行选择,不需要@Qualifier在注入点处添加注解。如果没有其他解析指示符(例如限定符或主标记),对于非唯一依赖关系的情况,Spring 会将注入点名称(即字段名称或参数名称)与目标 Bean 名称进行匹配,并选择同名的候选对象(如果有)(通过 Bean 名称或关联的别名)。

从 6.1 版本开始,这需要-parametersJava 编译器标志的存在。从 6.2 版本开始,容器会应用快速快捷解析来匹配 Bean 名称,当参数名称与 Bean 名称匹配且没有类型、限定符或主要条件覆盖匹配时,会绕过完整类型匹配算法。因此,建议您的参数名称与目标 Bean 名称匹配。

作为按名称注入的替代方案,请考虑使用 JSR-250@Resource注释,该注释在语义上定义为通过其唯一名称标识特定目标组件,而声明的类型与匹配过程无关。@Autowired具有相当不同的语义:按类型选择候选 bean 后,String 仅在那些类型选择的候选者中考虑指定的限定符值(例如,将account限定符与标有相同限定符标签的 bean 进行匹配)。

对于本身定义为集合、Map或数组类型的 Bean,@Resource 通过唯一名称引用特定的集合或数组 Bean 是一个不错的解决方案。也就是说,您也可以Map通过 Spring 的 @Autowired类型匹配算法来匹配集合、 和数组类型,只要元素类型信息保留在@Bean返回类型签名或集合继承层次结构中即可。在这种情况下,您可以使用限定符值在相同类型的集合中进行选择,如上一段所述。

@Autowired也考虑使用自身引用进行注入(即引用当前注入的 bean)。

@Autowired适用于字段、构造函数和多参数方法,允许在参数级别通过限定符注解进行缩小范围。相比之下,@Resource 仅支持具有单个参数的字段和 Bean 属性设置器方法。因此,如果您的注入目标是构造函数或多参数方法,则应坚持使用限定符。

自定义Qualifier注解

您可以创建自己的自定义限定符注解。为此,请定义一个注解并@Qualifier在定义中提供该注解,如以下示例所示:

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

	String value();
}

然后,您可以在自动装配的字段和参数上提供自定义限定符,如以下示例所示:

public class MovieRecommender {

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

	private MovieCatalog comedyCatalog;

	@Autowired
	public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
		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 中提供限定符元数据。具体而言,请参阅使用注解提供限定符元数据

在某些情况下,使用不带值的注解可能就足够了。当注解服务于更通用的用途,并且可以应用于多种不同类型的依赖项时,这种方法非常有用。例如,您可以提供一个离线目录,以便在没有互联网连接时进行搜索。首先,定义一个简单的注解,如下例所示:

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

然后将注解添加到要自动装配的字段或属性,如下例所示:

public class MovieRecommender {

	@Autowired
	@Offlineprivate MovieCatalog offlineCatalog;

	// ...
}

① 此行添加@Offline注解 现在 bean 定义只需要一个限定符type,如下例所示:

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

①此元素指定限定符。 您还可以定义自定义限定符注解,除了简单属性之外,还可以接受命名属性value。如果在要自动装配的字段或参数上指定了多个属性值,则 Bean 定义必须匹配所有这些属性值,才能被视为自动装配候选对象。例如,请考虑以下注解定义:

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

	String genre();

	Format format();
}

在这种情况下Format是一个枚举,定义如下:

public enum Format {
	VHS, DVD, BLURAY
}

要自动装配的字段用自定义限定符注释,并包含两个属性的值:genreformat,如以下示例所示:

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;

	// ...
}

最后,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>