【Spring】深入解析 bean 的属性填充 二 AutowiredFieldElement#inject
前言
接上文,本章重点解读 InjectedElement#inject
:
- 针对属性注入的
AutowiredFieldElement#inject
- 针对方法注入的
AutowiredMethodElement#inject
AutowiredFieldElement#inject
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
// 已缓存
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
// 构造 DependencyDescriptor
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
// 获取 TypeConverter
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
// 委托给 beanFactory.resolveDependency 方法获取
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
}
// 缓存...
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
AutowiredFieldElement#inject
就是针对属性的注入:
DependencyDescriptor
是对属性依赖的描述类,封装诸如当前属性的信息、必填性等- 属性的获取与
byType
填充方式相似,最终也是委托给AutowireCapableBeanFactory#resolveDependency
方法,它的核心实现在DefaultListableBeanFactory
中,接下来我们就重点解读该方法 - 最后基于反射给属性赋值
DefaultListableBeanFactory#resolveDependency
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// 参数名称发现器
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
/**
* 此处是针对不同的类型对返回值进行处理,但核心逻辑依然是收口
* 到 doResolveDependency 方法
*/
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
// ...
}
/**
* 此分支才是我们最常用的,核心逻辑在 doResolveDependency
*/
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
- 这里针对不同类型的元素,对返回结果进行对应处理
- 最终的核心逻辑还是收口在
doResolveDependency
方法
doResolveDependency
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
// ShortcutDependencyDescriptor 处理,即缓存处理
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
Class<?> type = descriptor.getDependencyType();
// 如果是 @Value 注解元素,则获取 value 值
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
if (value instanceof String) {
// 占位符解析
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
// SpEL 解析
value = evaluateBeanDefinitionString(strVal, bd);
}
// 类型转换后返回
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
try {
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}
catch (UnsupportedOperationException ex) {
}
}
/**
* 对于返回类型是 Array Collection Map 类型的属性,
* 会对基于 findAutowireCandidates 获取的实例进行类型转换后直接返回
*/
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
/**
* 核心方法 findAutowireCandidates:获取所有符合
* 注入条件的实例
*/
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
// 如果没有,抛出异常 expected at least 1 bean which qualifies as autowire candidate.
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
/**
* 如何元素个数多余一个,则委托 determineAutowireCandidate 方法
* 进行再次筛选
* 如果筛选结果为空,但接受元素不是集合类型,则抛出异常
* 如果筛选出来了,那么就最终返回
*/
if (matchingBeans.size() > 1) {
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
}
else {
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
// ...
return result;
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
核心逻辑实现,结合代码注释,这里再做个总结:
- 之前会对已经解析的属性缓存对应的
ShortcutDependencyDescriptor
,此处会有体现 - 正片开始,第一步是解析
@Value
注解,这里有几个核心步骤,比如占位符的解析
,它让@Value
注解支持从Environment
中直接获取对应属性,再比如SpEL
表达式的解析,不仅支持各种常见表达式解析,甚至如同我们之前演示的示例获取容器中的bean示例
(@Value("#{a}")
) - 接下来就是
@Autowired
注解的处理,首先针对集合类型的属性,会直接对findAutowireCandidates
获取的备选属性进行类型转换后直接返回,这就是之前的示例中为什么我们可以注入此类属性Map<String, BasicPOJO> map
- 其中
findAutowireCandidates
方法用来返回所有满足注入条件的示例,下文会重点解读 - 接下来,如果根本没有备选实例且必须注入,则抛出异常
- 反之如果有多个备选示例,则由
determineAutowireCandidate
尝试确定唯一实例,下文会单独解读 - 如果最终可以确定唯一元素,则成功返回
findAutowireCandidates
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
// 获取所有指定类型的 beanName(包括父级BeanFactory)
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
/**
* 对于注册在 resolvableDependencies 中的元素,此处会加入备选
* 容器中默认注册的元素如下:
* beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
* beanFactory.registerResolvableDependency(ResourceLoader.class, this);
* beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
* beanFactory.registerResolvableDependency(ApplicationContext.class, this);
*/
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
Object autowiringValue = classObjectEntry.getValue();
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
/**
* 对于
* 1)不是自引用(大概就是自己注入自己这种情况)
* 并且 2)基于 AutowireCandidateResolver 进行筛选
* 通过的元素加入备选
*/
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
/**
* 如果第一次筛选没有没有备选实例,此处会重新生成 DependencyDescriptor
* 再次筛选,针对该 DependencyDescriptor 会放弃泛型精确匹配
*/
if (result.isEmpty()) {
boolean multiple = indicatesMultipleBeans(requiredType);
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
// 若仍旧没有备选示例,则考虑自引用
if (result.isEmpty() && !multiple) {
for (String candidate : candidateNames) {
if (isSelfReference(beanName, candidate) &&
(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
}
}
return result;
}
该方法略长,结合注释进行概括:
- 对于注册在
resolvableDependencies
中的元素,此处会加入备选,容器会在启动阶段注册一些默认的ResolvableDependency
进去,见注释,可以理解为几组默认的类型 -> bean实例
- 第一轮筛选:1)不能是自引用,即自身注入自身 2)交给
AutowireCandidateResolver
进一步筛选,此处会进行诸如泛型
限定符
的匹配,下一章节详细解读 - 若筛选结果为空,则放弃泛型的精确匹配再筛选一次
- 若筛选结果还为空,则考虑自引用
- 最后无论如何返回即可
可以发现,核心的筛选逻辑由 AutowireCandidateResolver
实现,诸如示例中针对泛型的精确匹配,再比如 @Qualifier
注解的限定符匹配等
determineAutowireCandidate
@Nullable
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
// 筛选出被 @Primary 注解修饰的实例
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
// 根据实现的 Priority 接口进行优先级过滤
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
/**
* 最后,如果是 resolvableDependencies 则优先返回
* 否则进行 name 匹配
*/
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
matchesBeanName(candidateName, descriptor.getDependencyName())) {
return candidateName;
}
}
return null;
}
之前提到,对于返回多个备选实例的情况,若接受属性非集合,则要进行优先级筛选:
- 若实例被
@Primary
注解修饰,则优先 - 根据实现的
Priority
接口进行优先级筛选 - 最后,才会根据
beanName
和元素名称进行匹配,这就是为什么示例配置中BasicPOJO a
属性会成功注入
总结
整体概括一下:
基于 @Autowired
注解驱动的依赖注入类似于 byType
自动依赖注入的方式,因而必然存在多个匹配的情况,当接受属性是集合数组等类型如 Map
Array
时可以全部注入,相反地对于单个属性,则依次进行诸如 @Primary
属性名称
等的匹配
其中 DefaultListableBeanFactory#findAutowireCandicate
方法获取所有备选实例,其中重要的筛选逻辑诸如 泛型匹配
限定符匹配
等是委托给 AutowireCandidateResolver
实现的,这也是我们下章重点解读的对象
基于 @Resource 注解驱动的依赖注入类似于 byName 自动依赖注入的方式,相对类型的注入
逻辑会清晰一点,毕竟 name 跟 bean 理论上一对一,就不涉及复杂的筛选逻辑了
参考
【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析