跳到主要内容
版本:7.0.3

基于注释的微调与限定符自动连接

Hunyuan 7b 中英对照 Fine-tuning Annotation-based Autowiring with Qualifiers

@Primary@fallback 是利用类型自动绑定的有效方式,当可以确定一个主要(或非备用)候选者时,这些注解可以在多个实例中发挥作用。

当你需要对选择过程有更多的控制时,可以使用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"/> // <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>
  • 带有 main 限定符值的 Bean 会与具有相同限定符值的构造函数参数关联。

  • 带有 action 限定符值的 Bean 会与具有相同限定符值的构造函数参数关联。

对于回退匹配(fallback match),bean名称被视为默认的限定符值。因此,你可以使用idmain来定义bean,而无需使用嵌套的限定符元素,这样也能得到相同的匹配结果。然而,尽管你可以利用这种约定来按名称引用特定的bean,但@Autowired本质上是基于类型的注入(type-driven injection),并允许使用可选的语义限定符(semantic qualifiers)。这意味着,即使使用bean名称作为回退选项,限定符值在类型匹配的过程中始终具有“缩小范围”的作用(即它们只能从更具体的类型中筛选出符合条件的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名称相匹配。

作为按名称注入的替代方案,可以考虑JSR-250中的@Resource注解,该注解的语义定义是通过组件的唯一名称来识别特定的目标组件,声明的类型对于匹配过程来说并不重要。而@Autowired则具有不同的语义:在根据类型选出候选bean之后,才会考虑所指定的String限定符值(例如,将account这个限定符与带有相同限定符标签的bean进行匹配)。

对于那些本身就是集合(Collection)、映射(Map)或数组(Array)类型的bean,使用@Resource是一个很好的解决方案,可以通过唯一名称来引用特定的集合或数组bean。不过,只要在@Bean的返回类型签名或集合继承层次结构中保留了元素类型信息,你也可以通过Spring的@Autowired类型匹配算法来匹配集合、映射和数组类型。在这种情况下,你可以使用限定符值在同一类型的集合中进行选择,如前一段所介绍的那样。

@Autowired 也会考虑自我引用进行注入(即,引用当前正在被注入的 Bean)。详情请参见 自我注入

@Autowired 可以应用于字段、构造函数和多参数方法,允许通过参数级别的限定符注解来进一步限定目标。相比之下,@Resource 仅支持用于字段以及只有一个参数的 Bean 属性设置器方法。因此,如果你的注入目标是构造函数或多参数方法,那么你应该使用限定符。

你可以创建自己的自定义限定符注解。为此,需要定义一个注解,并在定义中提供@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/>标签的子元素添加,然后指定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中提供限定符元数据。具体来说,请参阅使用注解提供限定符元数据

在某些情况下,使用没有值的注释可能就足够了。当注释具有更通用的用途,并且可以应用于几种不同类型的依赖关系时,这种方法会很有用。例如,你可以提供一个离线目录,在没有互联网连接时也可以进行搜索。首先,按照以下示例定义简单的注释:

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

然后按照以下示例,在需要自动绑定的字段或属性上添加注释:

public class MovieRecommender {

@Autowired
@Offline 1
private MovieCatalog offlineCatalog;

// ...
}
  • 这一行添加了 @Offline 注解。

现在,bean 定义只需要一个限定符 type,如下例所示:

<bean class="example.SimpleMovieCatalog">
<qualifier type="Offline"/> // <1>
<!-- 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 是一个枚举(enum),定义如下:

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>