Spring 注解原理(三)@Qualifier @Value
Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)
一、AutowireCandidateResolver 接口
AutowireCandidateResolver 用来判断一个给定的 bean 是否可以注入,最主要的方法是 isAutowireCandidate
public interface AutowireCandidateResolver {
// 判断给定的 bdHolder 是否可以注入 descriptor,BeanDefinition#autowireCandidate 默认为 true
// DependencyDescriptor 是对字段、方法、参数的封装,便于统一处理,这里一般是对属性写方法参数的封装
default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
return bdHolder.getBeanDefinition().isAutowireCandidate();
}
// @since 5.0 判断是否必须注入,如果是字段类型是 Optional 或有 @Null 注解时为 false
default boolean isRequired(DependencyDescriptor descriptor) {
return descriptor.isRequired();
}
// @since 5.1 判断是否有 @Qualifier(Spring 或 JDK) 或自定义的注解
default boolean hasQualifier(DependencyDescriptor descriptor) {
return false;
}
// @Value 时直接返回
default Object getSuggestedValue(DependencyDescriptor descriptor) {
return null;
}
// @since 4.0
default Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
return null;
}
}
AutowireCandidateResolver 的实现有以下几个:
SimpleAutowireCandidateResolver
相当于一个简单的适配器GenericTypeAwareAutowireCandidateResolver
进一步判断泛型是否匹配QualifierAnnotationAutowireCandidateResolver
处理 @Qualifier 和 @Value 注解ContextAnnotationAutowireCandidateResolver
处理 @Lazy 注解,重写了 getLazyResolutionProxyIfNecessary 方法。
这里主要看一下 QualifierAnnotationAutowireCandidateResolver 是如何处理 @Qualifier 和 @Value 注解的。
二、QualifierAnnotationAutowireCandidateResolver
2.1 @Value 处理
在 Spring 中可以使用 @Value 注入配置属性
@Value("${jdbd.url}")
private String url;
@Value 的处理方式如下,查找到 @Value 的值作为 AutowireCandidateResolver#getSuggestedValue 的返回值。
private Class<? extends Annotation> valueAnnotationType = Value.class;
@Override
public Object getSuggestedValue(DependencyDescriptor descriptor) {
// findValue 用于提取 valueAnnotationType 注解的值
Object value = findValue(descriptor.getAnnotations());
if (value == null) {
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
value = findValue(methodParam.getMethodAnnotations());
}
}
return value;
}
如果 getSuggestedValue 返回了值就直接返回了。
2.3 @Qualifier 处理
在使用 Spring 框架中进行自动注人时,Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。当找不到一个匹配的 Bean 时,Spring 容器将抛出 BeanCreationException 异常。Spring 允许我们通过 Qualifier 指定注入 Bean 的名称,这样歧义就消除了。@Qualifier 四种用法介绍
@Autowire
@Qualifier("person")
private Person person;
2.3.1 默认处理 Spring 和 JDK 的 @Qualifier 注解
private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<>(2);
public QualifierAnnotationAutowireCandidateResolver() {
this.qualifierTypes.add(Qualifier.class);
try {
// JSR-330
this.qualifierTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Qualifier",
QualifierAnnotationAutowireCandidateResolver.class.getClassLoader()));
} catch (ClassNotFoundException ex) {
}
}
2.3.2 isAutowireCandidate
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
boolean match = super.isAutowireCandidate(bdHolder, descriptor);
if (match) {
// 检验是否满足 qualifierTypes 集合中定义的规范
// descriptor 一般封装的是属性写方法的参数,即方法参数上的注解
match = checkQualifiers(bdHolder, descriptor.getAnnotations());
if (match) {
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
Method method = methodParam.getMethod();
if (method == null || void.class == method.getReturnType()) {
// 方法上的注解
match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
}
}
}
}
return match;
}
最核心的方法是 checkQualifiers(bdHolder, descriptor.getAnnotations()),用于校验所有的 @Qualifier 是否合法,这个方法还有些没看明白,以后再研究。
protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
if (ObjectUtils.isEmpty(annotationsToSearch)) {
return true;
}
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
for (Annotation annotation : annotationsToSearch) {
Class<? extends Annotation> type = annotation.annotationType();
boolean checkMeta = true;
boolean fallbackToMeta = false;
// 1. 第一步:存在 @Qualifier
if (isQualifier(type)) {
if (!checkQualifier(bdHolder, annotation, typeConverter)) {
fallbackToMeta = true;
} else {
checkMeta = false;
}
}
// 2. 第二步:两种情况下执行,一是存在 @Qualifier 校验失败;二是不存在 @Qualifier
if (checkMeta) {
boolean foundMeta = false;
for (Annotation metaAnn : type.getAnnotations()) {
Class<? extends Annotation> metaType = metaAnn.annotationType();
if (isQualifier(metaType)) {
foundMeta = true;
// 1. 第一步校验失败下元注解上的 @Qualifier 没有配置 value 直接返回 false
// 2. 不管第一步执行没有,如果检验失败直接返回 false
if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||
!checkQualifier(bdHolder, metaAnn, typeConverter)) {
return false;
}
}
}
// 第一步说明存在 @Qualifier 则必须校验成功,否则直接返回
if (fallbackToMeta && !foundMeta) {
return false;
}
}
}
return true;
}
最后一步是 checkQualifier(bdHolder, annotation, typeConverter) 将 bd.qualifiers 的信息和当前 @Qualifier 注解的信息进行对比,看是否一致。大多数情况下 bd.qualifiers 为空,除非手动进行以下配置:
<bean id="mysqlDataSourceBean" class="com.bean.MysqlDriveManagerDataSource">
<!-- qualifier 标签默认 type=org.springframework.beans.factory.annotation.Qualifier -->
<qualifier value="mysqlDataSource"/>
</bean>
具体较验如下:
protected boolean checkQualifier(
BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
Class<? extends Annotation> type = annotation.annotationType();
RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition();
// 1. 获取 BeanDefinition 中的 qualifier
AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
if (qualifier == null) {
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
}
if (qualifier == null) {
// First, check annotation on qualified element, if any
Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type);
// Then, check annotation on factory method, if applicable
if (targetAnnotation == null) {
targetAnnotation = getFactoryMethodAnnotation(bd, type);
}
if (targetAnnotation == null) {
RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd);
if (dbd != null) {
targetAnnotation = getFactoryMethodAnnotation(dbd, type);
}
}
if (targetAnnotation == null) {
// Look for matching annotation on the target class
if (getBeanFactory() != null) {
try {
Class<?> beanType = getBeanFactory().getType(bdHolder.getBeanName());
if (beanType != null) {
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type);
}
}
catch (NoSuchBeanDefinitionException ex) {
// Not the usual case - simply forget about the type check...
}
}
if (targetAnnotation == null && bd.hasBeanClass()) {
targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type);
}
}
if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
return true;
}
}
// 2. bd.qualifier 中的属性和 @Qualifier 中配置的进行对比
// attributes 为 @Qualifier 注解信息,value 有一个默认值 ""
// qualifier 为 bd 中的配置,默认为 null
Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
if (attributes.isEmpty() && qualifier == null) {
// 当注解中属性值为空时,如自定义的 @Qualifier,此时 bd.qualifier 必须存在
return false;
}
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
String attributeName = entry.getKey();
Object expectedValue = entry.getValue();
Object actualValue = null;
// Check qualifier first
if (qualifier != null) {
actualValue = qualifier.getAttribute(attributeName);
}
if (actualValue == null) {
// Fall back on bean definition attribute
actualValue = bd.getAttribute(attributeName);
}
// 如果 actualValue=null(如没有 qualifier),默认是和 bdHolder 中 bean 的名称进行比较
if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) &&
expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) {
// Fall back on bean name (or alias) match
continue;
}
if (actualValue == null && qualifier != null) {
// Fall back on default, but only if the qualifier is present
actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName);
}
if (actualValue != null) {
actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass());
}
if (!expectedValue.equals(actualValue)) {
return false;
}
}
return true;
}
总结一下,bd.qualifiers 的信息和当前 @Qualifier 注解校验规则如下:
获取 BeanDefinition 中定义的 qualifier 属性,如果没有配置,则尝试从其 factory-method 或 beanClass 上获取 @Qualifier 注解(可能为自定义),如果获取到就直接返回。
我们使用的大多数场景是没有配置 BeanDefinition#qualifier 属性的,也就是说 qualifier=null,下面进一步比较 bd.qualifier 中的属性和 @Qualifier 中的属性信息 attributes。如果注解没有属性字段,则必须配置 BeanDefinition#qualifier
将 bd.qualifier 和 attributes 中的属性字段逐一对比,默认是和 bd.beanName 进行对比,如果值不相等则抛出异常,也就是说可以在自定义注解中定义多个字段进行校验。如:
// 自定义的没有字段信息则必须显示的配置在 bd.qualifier 中
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
private static @interface TestQualifier {
}
// 会逐一对比 @TestQualifier 和 bd 中的字段信息,必须相等才能匹配
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
private static @interface TestQualifier {
String specialName default "";
}
参考:
- 《Spring 注解实现 Bean 依赖注入之 @Qualifier》:https://blog.csdn.net/lovin_fang/article/details/78537547
每天用心记录一点点。内容也许不重要,但习惯很重要!