对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
使用 Qualifiers 微调基于 Annotation 的自动装配
@Primary
是在多个实例中使用 autowiring by type 的有效方法,当一个实例
可以确定主要候选人。当您需要对选择过程进行更多控制时,
您可以使用 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
annotation 在单个构造函数参数上或
method 参数,如以下示例所示:
-
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 qualifier 值与 constructor 参数连接,该
使用相同的值进行限定。 |
2 | 具有action qualifier 值与 constructor 参数连接,该
使用相同的值进行限定。 |
对于回退匹配,Bean 名称被视为默认限定符值。因此,您
可以使用id
之main
而不是嵌套的限定符元素,而是 leading
更改为相同的匹配结果。但是,尽管您可以使用此约定来引用
按名称指定 bean,@Autowired
从根本上讲,是关于类型驱动注入的
可选的语义限定符。这意味着限定符值,即使 bean 名称为
fallback 在类型匹配集中始终具有缩小语义。他们没有
在语义上表示对唯一 Bean 的引用id
.好的限定符值为main
或EMEA
或persistent
,表示特定组件的特征,这些特征是
独立于 Beanid
,在匿名 bean 的情况下可能会自动生成
定义,例如前面示例中的定义。
限定符也适用于类型化集合,如前所述 — 例如,到Set<MovieCatalog>
.在这种情况下,所有匹配的 bean 都会根据声明的
限定符作为集合注入。这意味着限定符不必是
独特。相反,它们构成了筛选标准。例如,您可以定义
倍数MovieCatalog
具有相同限定符值 “action” 的 bean,所有这些 bean 都是
注入到Set<MovieCatalog>
注解@Qualifier("action")
.
在类型匹配中,让限定符值根据目标 Bean 名称进行选择
应聘者不需要 这需要 |
作为按名称注入的替代方法,请考虑使用 JSR-250@Resource
注解
它在语义上定义以通过其唯一名称标识特定的目标组件,
声明的类型与匹配过程无关。@Autowired
有
不同的语义:按类型选择候选 bean 后,指定的String
限定符值仅在类型选定的候选项中考虑(例如,
匹配account
限定符对标有相同限定符标签的 bean)。
对于本身定义为集合的 bean,Map
或数组类型、@Resource
是一个很好的解决方案,通过唯一名称引用特定的集合或数组 bean。
也就是说,从 4.3 开始,您可以匹配 collection、Map
和数组类型通过 Spring 的@Autowired
类型匹配算法,只要元素类型信息
保存在@Bean
返回类型签名或集合继承层次结构。
在这种情况下,你可以使用限定符值在相同类型的集合中进行选择。
如上一段所述。
从 4.3 开始,@Autowired
还会考虑注入的自引用(即引用
返回到当前注入的 bean)。请注意,自注入是一种后备。
对其他组件的常规依赖项始终具有优先权。从这个意义上说,自我
参考文献不参与常规的候选选择,因此位于
特别 从 不 主要。相反,它们总是以最低优先级结束。
在实践中,您应该仅将自引用作为最后的手段(例如,对于
通过 Bean 的事务代理调用同一实例上的其他方法)。
在这种情况下,请考虑将受影响的方法分解为单独的委托 Bean。
或者,您可以使用@Resource
,这可能会获取返回到当前 Bean 的代理
通过其唯一名称。
尝试注入 |
@Autowired
应用于字段、构造函数和多参数方法,允许
通过参数级别的限定符注释缩小范围。相比之下,@Resource
仅支持具有单个参数的字段和 Bean 属性 setter 方法。
因此,如果您的注入目标是
constructor 或多参数方法。
您可以创建自己的自定义限定符注释。为此,请定义一个 annotation 和
提供@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>
在 Classpath Scanning 和 Managed Components 中,您可以看到 在 XML 中提供限定符元数据。具体而言,请参阅提供带有注释的限定符元数据。
在某些情况下,使用没有值的 Comments 可能就足够了。这可以是 当注释用于更通用的用途并且可以应用于 几种不同类型的依赖项。例如,您可以提供离线 目录,当没有 Internet 连接可用时可以搜索。首先,定义 简单注释,如下例所示:
-
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 | 此元素指定限定符。 |
您还可以定义自定义限定符注释,这些注释接受
加法 or 代替简单的value
属性。如果多个属性值为
然后在要自动装配的字段或参数上指定,则 Bean 定义必须匹配
所有此类属性值都被视为 Autowire 候选项。例如,
请考虑以下注释定义:
-
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
}
要自动装配的字段使用 custom 限定符进行批注,并包含值
对于这两个属性: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/>
标记(如果不存在此类限定符),如
以下示例:
<?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>