概述
本文是对于《spring中Bean的生命周期之属性填充(1)》一些细节上的补充
属性注入
当spring获取完当前对象的所有注入点后,会在后续的生命周期中对依赖的属性进行注入,这里会根据注入点,生成一个属性描述对象
在根据这个属性描述对象获取到需要注入的Bean
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
参数
DependencyDescriptor descriptor:属性描述,但是如果有缓存传入的是ShortcutDependencyDescriptor类型
如果没有缓存,传入的是DependencyDescriptor类型
String requestingBeanName:当前创建bean的名字
Set autowiredBeanNames:要注入的bean
TypeConverter typeConverter:类型转换器
获取的核心方法:doResolveDependency
在进入核心的方法先这里会先处理Optional 和 ObjectFactory 这两个类型的Bean
Optional,这个类型其就是对要注入的bean进行了一次包装,使用如下
@Component
public class Aaa{
@Autowired
private Option<Bbb> bbb;
public Bbb test(){return bbb.get();}
}
在正真获取Option 中的Bbb 的是偶是需要先进行get()将泛型对象取出
对于ObjectFactory而言,我们先看使用
@Component
public class Aaa{
@Autowired
private ObjectFactory<Bbb> bbb;
public Bbb test(){return bbb.getObject();}
}
@Override
public Object getObject() throws BeansException {
if (this.optional) {
return createOptionalDependency(this.descriptor, this.beanName);
}
else {
Object result = doResolveDependency(this.descriptor, this.beanName, null, null);
if (result == null) {
throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
}
return result;
}
}
关于ObjectFactory的使用,可以解决在单例bean里面使用原型的Bean,在找注入的对象的时候,就会判断,是不是ObjectFactory类型,如果是就会马上new 一个DependencyObjectProvider 这个对象返回,这个对象其实是ObjectFactory的子类 ,然后会将这个需要注入的对象进行返回,在spring后续的生命周期中就会进行注入。所以上面test方法中的调用的getObject方法其实就是调用DependencyObjectProvider 的getObject方法,然后再getObject这个方法里面每次都会通过doResolveDependency去找注入点,所以返回的结果就是不一样的
doResolveDependency
在这个方法里面首先会调用 resolveShortcut
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
descriptor就是属性描述对象,前面说过,这里传过来的类型有两种可能
通过查看源码可以发现 DependencyDescriptor 会直接返回null
而ShortcutDependencyDescriptor返回的是 beanFactory.getBean(this.shortcut, this.requiredType);
当然这个方法最主要的功能就是找到一个要注入的bean
首先是获取@Value注解上配置的值,我们之前说过,只要是加了@Autowired,@Value,@Inject之一的就会被当成一个注入点
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); // 是否通过@Value注解指定了值
if (value != null) {
if (value instanceof String) {
// 先进行占位符的填充,解析"$"符号
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
// 解析Spring EL表达式,解析"#"符号(可以进行运算,可以写某个bean的名字)
value = evaluateBeanDefinitionString(strVal, bd);
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
try {
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}
catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
}
获取到@Value中的值过后,就会开始解析这个注解了
回对@Value里面的$和#进行解析
${}里面的内容就相当于key,就会根据这个key在spring的Environment里面去找对应的value
#{}就是spring提供的表达式,这里面提供了非常丰富的功能
但是即使进过了spring的解析,但是返回出来的value可能是一个对象,也有可能是一个字符串,
这里就会用到spring 的类型转换器(TypeConverter),如果返回的是一个字符串,那么类型转换器就会尝试的将这个字符串转换为要注入的对象的类型,如果说有对应的类型转换器并且转换成功,那就可以正常返回,但是如果说没有或者解析失败就会报错
所以这里也说明了spring是先处理@Value注解,如果有的话,在@Value就会返回
那么没有@Value注解呢?其实就是来到了@Autiwired本身的工作原理,先byType在byName
但是在此之前还有一步需要处理,就是对Map,数组,集合类型单独进行处理
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
如果特殊类型处理完后返回的结果是null,那么说明就是一个普通类型,就可以进入@Autowired的逻辑了
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
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();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {
// 调用beanFactory.getBean(beanName);创建bean对象
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
return result;
在后续的步骤中首先会根据类型去找要注入的bean,然后把找到的bean放入matchingBeans 这个map里面
如果说只找到了一个Bean,那么就不需要再进行byName了,就会将找到bean的名字加入缓存
这个instanceCandidate 这个变量比较有意思,这个变量是Object类型,当spring在找bean的时候返回的是bean的Class对象
因为这个找bean是可能找到多个的,但是注入只会注入一个,如果每个bean都要返回具体的对象,那么都需要去getBean,得不偿失
所以直接就返回Class类型
在只找到一个需要注入的bean后,就会将bean的名字传入resolveCandidate这个方法,在这个方法里面会调用getBean方法返回一个具体的以可进行注入的对象,然后还会将这个对象赋值给instanceCandidate ,随后将这个结果返回
但是如果我们这里找到了多个类型匹配的Bean,那么就会调用determineAutowireCandidate来找到那个唯一的一个bean,也就是进行筛选。
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
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;
}
这里的筛选首先判断是不是某个bean上加入了@Primary注解,如果加了这个注解,就返回加了这个注解的bean
如果没有,就会去判断优先级(@priority),但是值得注意的是@priority这个注解的判断是基于类的,也就是说,如果我们使用的是同一个类进行判断优先级是没有用的,取优先级高的bean进行返回,最后才是根据byName,根据bean的名字进行筛选
如果筛选完后,结果是空,那么就会判断这个Bean是不是一定需要注入,如果一定需要注入,那么就会报错