|
对于最新的稳定版本,请使用 Spring Framework 7.0.6! |
使用限定符微调基于注解的自动连线
@Primary 是在确定一个主要候选者时,通过类型使用自动连线的有效方法。当您需要对选择过程有更多控制时,可以使用 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 名称被视为默认的限定符值。因此,您可以将 bean 定义为 id 的 main,而不是嵌套的限定符元素,从而得到相同的匹配结果。然而,尽管您可以使用此约定通过名称引用特定的 bean,@Autowired 本质上是基于类型的注入,带有可选的语义限定符。这意味着即使有 bean 名称的回退,限定符值在类型匹配集合中始终具有缩小语义。它们并不语义上表示对唯一 bean 的引用 id。良好的限定符值是 main 或 EMEA 或 persistent,它们表达了特定组件的特征,这些特征与 bean id 无关,后者在匿名 bean 定义的情况下(如前面示例中的情况)可能会被自动生成。
限定符也适用于类型化集合,如前所述——例如,对Set<MovieCatalog>。在这种情况下,根据声明的限定符,所有匹配的 bean 会被注入为一个集合。这意味着限定符不必是唯一的。相反,它们构成过滤条件。例如,您可以定义多个带有相同限定符值“action”的MovieCatalog bean,所有这些 bean 都会注入到用@Qualifier("action")注解的Set<MovieCatalog>中。
|
允许限定符值在类型匹配的候选者中选择目标bean名称,不需要在注入点使用<code>0</code>注解。 如果没有其他解析指示器(例如限定符或主标记),对于非唯一依赖情况,Spring会将注入点名称(即字段名称或参数名称)与目标bean名称进行匹配,并选择同名的候选者(如果有的话,可以通过bean名称或关联别名)。 这需要存在 |
作为按名称注入的替代方法,可以考虑JSR-250 @Resource 注解,
该注解在语义上被定义为通过其唯一名称来标识特定的目标组件,
声明的类型对于匹配过程来说是无关的。@Autowired 的语义则有所不同:
在按类型选择候选 Bean 后,仅在这些按类型选择的候选 Bean 中考虑指定的 String 限定符值(例如,将 account 限定符与带有相同限定符标签的 Bean 进行匹配)。
对于本身定义为集合、Map 或数组类型的 Bean,@Resource 是一个很好的解决方案,可以通过唯一名称引用特定的集合或数组 Bean。
不过,从 4.3 版本开始,只要元素类型信息在 @Bean 的返回类型签名或集合继承层次结构中得到保留,你也可以通过 Spring 的 @Autowired 类型匹配算法来匹配集合、Map 和数组类型。
在这种情况下,你可以使用限定符值来在相同类型的集合之间进行选择,如上一段所述。
从 4.3 版本开始,@Autowired 也考虑自引用的注入(即对当前注入的 bean 的引用)。请注意,自引用是一种回退机制。对其他组件的常规依赖始终具有优先权。从这个意义上说,自引用不参与常规候选选择,因此它们绝不会是主要的。相反,它们总是具有最低的优先级。在实际应用中,您应仅在最后不得已的情况下使用自引用(例如,通过 bean 的事务代理调用同一实例上的其他方法)。在这种情况下,可以将受影响的方法提取到一个单独的委托 bean 中。或者,您可以使用 @Resource,这可以通过其唯一名称获取对当前 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定义应包含匹配的限定符值。此示例还表明,您可以使用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>