Spring依赖注入之注入Bean获取详解
Spring依赖注入之注入Bean获取详解
依赖注入获取Bean过程
在上一篇笔记中记录了Sprng的依赖注入,在依赖注入中有一步操作还没有写,其实我也不太想来写这篇笔记的,这个spring的依赖注入的最重要的一步操作,从容器中获取bean来注入的这个过程是在脑壳大,太绕了,我自己已经看懂了spring是如何获取到的,但是太绕了,这笔记不知道从何写起,写了估计过段时间光看笔记也是看不懂的,还是得看源码才行,不管怎么样,养成一个好的习惯吧,还是简单写一下,spring的获取bean的过程最重要的一个方法是:
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
//这里判断是否有缓存,cached默认是false,当field第一次加载的时候是没有缓存的,当加载过后会放入缓存cachedFieldValue
//cachedFieldValue也是一个依赖描述器,具体为ShortcutDependencyDescriptor,可以理解为较块的方式获取bean对象
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
// 和xml那边一模一样的,就是先通过属性来构造一个依赖描述器,就是封装了field的所有属性
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
//设置依赖描述器的容器的bean class
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
//然后通过依赖描述器来找到一个bean,请注意,这里是属性的依赖注入,比如private UserService uservice
// //那么这里是来找到bean类下面的属性UserService在spring容器中的ben对象,也就是value
//resolveDependency在这里是先byType,再byName,当byType有多个的时候,就要进行byName,所以在
// //在spring中写注入@AutoWired对应的属性名称的时候尽量规范一点
/**
* 下面这个方法有点复杂,简单来说我们都知道它在干嘛,无非就是根据依赖描述器(字段信息)去容器获取一个bean对象
* desc:被注入对象的字段描述信息
* beanName:当前注入属性或者方法所在的bean对象的beanName
* autowiredBeanNames:被注入对象所在的beanName集合(比如某个bean中 写private UserService userService, 如果beanName
* =userService,那么autowiredBeanNames中就会存入userService这个名字)
* typeConverter:转换器,如果发现注入的是一个@Value(“xxx”),那么这个xxx是否有转换器进行转换
* 在这个方法里面的逻辑我大概罗列一下 :
* 1.处理@Value注解,如果@Value有值,那么就会直接获取@Value的值,这个时候如果是占位符$,那么会从环境中Envirment
* 中获取一个对应的属性值注入,如果是#el表达式,那么会从spring容器中获取一个bean返回;
* 3.注入的类型是否是Map Collection Array,如果是就获取bean封装成Map or Collection or Array返回
* 2.然后会去容器中获取bean,如果获取的bean是一个,那么封装了直接返回,如果多个bean,这个时候就要进行处理:
* a.先筛选,通过是否是自动注入候选者筛选,然后在看是否有泛型,有的话对泛型的类型是否满足根据注入类型从容器中获取的类型进行判断;
* b.然后在判断@Qualifier,看是否有满足的@Qualifier名字匹配的bean,有的话过滤非@Qualifier配置的名字的bean; *
* a.看是否有@Primary注解,如果有,那么就选择具有@PrimaryKey注解所在的bean,注意的是相同的Bean不能同时存在@PrimaryKey注解
* b.如果没有再看有没有Priority优先级的,如果有优先级的注解,那么判断优先级的大小
* 优先级的数字越小,级别越高
* c.如果还是不能确定就开始byName
*
*
*/
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
//下面的逻辑是如果开启了缓存cached,默认是false,然后取出注入的属性名称,也就是beanName,然后判断,设置缓存字段值
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = desc;
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
}
}
if (value != null) {
//如果找到了注入的bena,然后通过反射进行注入
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
除了 value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);这行代码,其他在上一篇笔记中已经写了;总结下这个过程就是:
1.根据注入类型判断如果是ObjectFactory,返回一个ObjectFactory对象,程序员通过ObjectFactory.getObject来获取对象,如果是原型对象,那么每次获取的对象都不一样;也就是说通过一个原型bean对象,通过注入过后,每次执行getObject都是不一样的对象。
2.如果是Optional对象,然后获取一个Bean对象,封装成Optional对象返回;
3.如果是一般的注入对象,那么过程如下:
如果有@Value注解,处理@Value注解;
否则处理Map、Collection、Array类型的注入,如果是这些类型,那么会从容器获取bean然后封装成Map or Collection or Array类型,然后返回;
最后调用findAutowireCandidates方法得到根据注入类型找到的所有bean
a.isAutowireCandidate的验证(默认为true)
b.泛型的验证;
c.Qualifier的筛选;
通过这些验证过后得到了一个Map集合,Map集合就是符合条件的所有bean,如果这个Map只有一条数据那么直接简单处理返回,如果这个Map大于1的时候,就要进行进一步的唯一筛选,筛选过程如下:
找到有多个bean,这里的处理就比较麻烦一点了
determineAutowireCandidate确定唯一Bean的三步操作
1.检查是否有@Primary注解,如果有就取配置了@Primary注解的bean,如果发现有不止一个bean有@Primary,则报错;如果没有这注解,当没调用
2.检查是否有@Priority注解,如果有判断优先级,如果有两个bean的优先级相同则报错;如果没有这注解,当没调用
3.如果前两步还没有确定唯一的bean,那么最后一步就是byName,找到唯一的bean。
所以找bean的过程是:
byType->Map等集合处理->自动注入候选者验证->Qualifier的筛选->@Primary->@Priority->byName的过程
所以依赖注入不仅仅是先byType,再byName,这中间还有很多过程。
resolveDependency方法详解
spring的依赖注入中最核心的有三部分,第一步部分是找到注入点,第二部分是根据注入点的类型去容器中寻找合适的bean,去容器中找到的方法就是通过resolveDependency找到的,这个方法很复杂,涉及了很多处理,但是我们要只要把它的思路弄清楚,太细的东西就不要太纠结,这里我们通过源码来分析下具体的实现细节,这个方法的具体实现是在DefaultListableBeanFactory中实现的。
/**
* 这个方法比较复杂,可以说在spring的ioc中,算是复杂度排的上号的
* 这个方法最终会返回一个对象给我们,这里面包含了很多逻辑,首先通过byType查到bean对象,如果
* bean对象有多个,那么在byName的时候会经过6层筛选,
* 1.是否是自动注入候选者(autowire-candidate),默认为true;就是如果这个值是false则不不能进行自动注入;
* 2.是否有泛型,有的话,对泛型进行判断;
* 3.Qualifier判断
* 4.是否是Map、array类型的;
* aaa@qq.com注解,如果是@Primary注解,直接取加了@Primary注解的bean;
* 6优先级判断,@Priority判断,如果有优先级,取优先级最大的bean;
* 如果上面6步还不能筛选出唯一的bean,那么就开始byName进行筛选
*
* @param descriptor the descriptor for the dependency (field/method/constructor)
* @param requestingBeanName the name of the bean which declares the given dependency
* @param autowiredBeanNames a Set that all names of autowired beans (used for
* resolving the given dependency) are supposed to be added to
* @param typeConverter the TypeConverter to use for populating arrays and collections
* @return
* @throws BeansException
*/
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
//判断是否是Optional类型的,比如在类中这样写Optional<UserService> userService;也是可以的
//那么spring会将泛型的类型拿出来然后注入,最后封装成Optional对象返回
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
//下面这个是ObjectFactory类型,如果是ObjectFactory,和上面的optional类型差不多的处理方式,最后封装成一个ObjectFactory
} 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 {
/**
* 一般我们在bean中写普通的@AutoWired或者@value注解,一般注入的逻辑都会走到这里,上面的判断是根据
* 一些不是很常用的类型来判断,这里是比较常用的
*/
//这里是干什么的呢?这里是处理如果你的注入点属性或者方法是加了@Lazy注解,也就是延迟加载的,
//如果你设置了延迟加载,那么 spring在依赖注入的时候是不会给你真实的注入一个bean,而是创建一个代理对象
//当你真的需要获取注入的对象的时候,其实是通过代理对象实时获取的对象
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
descriptor, requestingBeanName);
if (result == null) {
//这里就是真正的去找bean
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
ContextAnnotationAutowireCandidateResolver:
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
/**
* 一个三元运算符,判断是否加了@Lazy注解,如果加了就直接创建一个代理对象返回,如果没有加@Lazy注解,那么
* 就返回一个空,前面调用的方法的逻辑继续往下走
*/
return (isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null);
}
/**
* 构建一个代理对象,当注入点的对象在获取对象的时候,其实是调用的getTarget方法返回的对象
* 可以看到getTarget方法也是调用的找bean的方法,也就是说这个过程在你使用的时候才去执行找bean的过程
* 而真正的注入的时候是返回一个代理对象
* 代理对象根据是是接口还是类,如果是接口,则吃用JDK的动态代理,如果是类,使用的是CGLIB代理
* @param descriptor
* @param beanName
* @return
*/
protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
"BeanFactory needs to be a DefaultListableBeanFactory");
final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
TargetSource ts = new TargetSource() {
@Override
public Class<?> getTargetClass() {
return descriptor.getDependencyType();
}
@Override
public boolean isStatic() {
return false;
}
@Override
public Object getTarget() {
Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
if (target == null) {
Class<?> type = getTargetClass();
if (Map.class == type) {
return Collections.emptyMap();
}
else if (List.class == type) {
return Collections.emptyList();
}
else if (Set.class == type || Collection.class == type) {
return Collections.emptySet();
}
throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
"Optional dependency not present for lazy injection point");
}
return target;
}
@Override
public void releaseTarget(Object target) {
}
};
ProxyFactory pf = new ProxyFactory();
pf.setTargetSource(ts);
Class<?> dependencyType = descriptor.getDependencyType();
if (dependencyType.isInterface()) {
pf.addInterface(dependencyType);
}
return pf.getProxy(beanFactory.getBeanClassLoader());
}
上面的的代码逻辑是;首先根据DependencyDescriptor来获取注入的字段或者方法的类型,是通过根据getDependencyType获取注入的类型,然后进行判断:
1.如果是Optional类型,那么调用createOptionalDependency,获取bean过后封装一个optional对象返回,程序员通过optional的get方法获取指定的bean对象;
2.如果是ObjectFactory类型,然后封装成一个ObjectFactory对象,这个ObjectFactory对象中重写了getObject方法,程序员可以根据getObject方法获取指定的注入类型;
3.如果都不是上面的两种类型,第三种类型我不知道是干嘛了,这里不写;如果不是ObjectFactory类型和Optional类型,则调用doResolveDependency类型。
Optional类型的获取bean
方法createOptionalDependency的具体实现细节如下:
private Optional<?> createOptionalDependency(
DependencyDescriptor descriptor, @Nullable String beanName, final Object... args) {
//NestedDependencyDescriptor开启了nestingLevel参数,可以获取泛型
DependencyDescriptor descriptorToUse = new NestedDependencyDescriptor(descriptor) {
@Override
public boolean isRequired() {
return false;
}
@Override
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {
return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) :
super.resolveCandidate(beanName, requiredType, beanFactory));
}
};
//调用普通的方法获取对象
Object result = doResolveDependency(descriptorToUse, beanName, null, null);
//最终封装成Optional对象返回
return (result instanceof Optional ? (Optional<?>) result : Optional.ofNullable(result));
}
这个方法比较简单,就是封装了NestedDependencyDescriptor,然后在NestedDependencyDescriptor重写了resolveCandidate方法,也就是在spring中获取bean,最后封装成了Optional对象返回。
ObjectFactory类型获取bean
/**
* Serializable ObjectFactory/ObjectProvider for lazy resolution of a dependency.
* 这个DependencyObjectProvider是用于在@AutoWire注解中使用的ObjectFactory<UserService> userService
* 这种格式,然后封装成一个DependencyObjectProvider,那么在使用的时候每次用getObject获取对象的时候,那么这个封装
* 类重写了ObjectFactory的getObject方法
*/
public DependencyObjectProvider(DependencyDescriptor descriptor, @Nullable String beanName) {
//而且是支持泛型的,就是可以支持ObjectFactory<Optional<UserService>>
//NestedDependencyDescriptor是开启了泛型的参数,就是可以获取泛型
this.descriptor = new NestedDependencyDescriptor(descriptor);
this.optional = (this.descriptor.getDependencyType() == Optional.class);
this.beanName = beanName;
}
就是封装了一个对象,然后这个对象继承了ObjectFactory,最后重写了getObject方法,具体的用法在注释上已经写明了。
doResolveDependency(普通注入类型)
当注入的类型不是Optional和ObjectFactory类型的时候,那么就进入了doResolveDependency这个方法,这个方法就是处理了@Value,是否自动注入候选者、泛型的处理、Qualifier、Map等集合的处理、@Primary、@Priority以及byName的处理;
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
//这里是前面调用的时候是否有缓存的逻辑,就是说前面有个参数叫cached,如果是true,
//那么它的依赖类型是ShortcutDependencyDescriptor,普通的是DependencyDescriptor
//所以注意到前面那段逻辑就知道这里是直接从容器里面读取了bean
Object shortcut = descriptor.resolveShortcut(this);
//如果这里获取到了,就直接返回
if (shortcut != null) {
return shortcut;
}
Class<?> type = descriptor.getDependencyType();
//获取@Value的值,是在QualifierAnnotationAutowireCandidateResolver中进行处理的
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
/**
* 下面的这个逻辑就是对@Value返回的值进行处理的
*/
if (value != null) {
/**
* 如果找到了值,那么就要判断了,因为@Value中只有一个属性value,这个value是string类型的
* 在字符串中有三种方式,分别是@Value("xxx"),@Value("${"xxx"}"),@Value("#{xxx}")
* 这三种方式的value处理是不一样的,首先第一种是直接当成一个字符串的值进行赋值;
* 第二种的方式是要从Environment中去获取xxx对应的value,而第三种方式是#el表达式,
* 那么第三种是直接将xxx作为一个beanName去spring容器中找bean对象
*/
if (value instanceof String) {
String strVal = resolveEmbeddedValue((String) value);
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
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的处理,下面的代码是对@AutoWired的处理
*/
//对Array、Collection、Map的处理;简单来说就是descriptor.getDependencyType()根据返回的类型,也就是注入的类型如果是
//Array,Collection、Map的类型,那么获取bean,调用findAutowireCandidates封装到Array、Collection、Map中返回
//比如你在注入的时候写了private Collection<UserService>这种格式,也是可以注入的
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
//注入的核心方法findAutowireCandidates,这个方法会返回一个Map,也就是说可以有一个,可以有多个,有多个的情况
//就是在spring容器中注入了1个以上的相同的Bean,很简单就是比如一个接口,被几个实现类实现了,那么也会有这种情况
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) {
/**
* 找到有多个bean,这里的处理就比较麻烦一点了
* determineAutowireCandidate确定唯一Bean的三步操作
* 1.检查是否有@Primary注解,如果有就取配置了@Primary注解的bean,如果发现有不止一个bean有@Primary,则报错;如果没有这注解,当没调用
* 2.检查是否有@Priority注解,如果有判断优先级,如果有两个bean的优先级相同则报错;如果没有这注解,当没调用
* 3.如果前两步还没有确定唯一的bean,那么最后一步就是byName,找到唯一的bean
*/
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
} else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
} else {
//代码逻辑到这里,证明上面的找到了唯一的一个Bean,直接处理下返回了
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
//将找到的beanName放入到要返回的map集合中
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
//在前面的方法写过,就是说再找bean的时候,有可能这个bean还没有被实例化也就是class类型,那个时候是没有实例化的,而是添加到map中
//等真正的确定了唯一的bean过后再次实例化,万一找到的不是你想要的那个bean,也就是不是要注入的bean,那么那个时候实例化就是浪费性能
if (instanceCandidate instanceof Class) {
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;
} finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
上面代码的处理逻辑我这边罗列一下:
1.首先根据 Object shortcut = descriptor.resolveShortcut(this)看是否能获取到bean对象,因为这个是在前面如果加入了缓存了,那么就会缓存一个ShortcutDependencyDescriptor对象,这个对象就是可以快速的从容器中获取一个bean,这个对象中的shortcut属性就是bean的beanName,当第一次加载的时候是没有这个缓存的,简单来说就是第一次缓存为false的时候会先去按照顺序也就是resolveDependency方法获取一个bean,然后设置缓存字段,如果后面的再来调用resolveDependency方法的时候,如果是这个类型,那么就会执行ShortcutDependencyDescriptor中的resolveShortcut犯法来尝试获取bean,如果获取到直接返回对象,前面的缓存的逻辑在inject这个方法里面
封装的逻辑在这个方法的后面:
2.如果第一步没有获取到,那么进入下面的处理@Value的逻辑,处理@Value的逻辑在源码的注释里面已经写的横清楚了,就是有三种情况,然后对@Value的三种情况进行处理。
3.对注入类型是Array、Collection、Map类型的进行处理;对Array、Collection、Map的处理;简单来说就是descriptor.getDependencyType()根据返回的类型,也就是注入的类型如果是,Array,Collection、Map的类型,那么获取bean,调用findAutowireCandidates封装到Array、Collection、Map中返回,比如你在注入的时候写了private Collection这种格式,也是可以注入的;
4.上面如果都还没找到bean,那么就会通过findAutowireCandidates方法进行确定bean,这个方法返回的是一个Map,key是beanName,Value可能是bean对象,可能是beanclass,因为这个方法会找到多个符合type(descriptor.getDependencyType())的bean对象或者beanclass,因为这个时候还没有确定是哪个,所以先不实例化,等确定了是哪个bean,如果是clas是,再进行实例化;在这个方法里面就会进行是否是自动注入的候选者、泛型的处理、@Qualifier的处理过程。
protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
//找到requiredType类型的所有bean的名字,在spring容器中找到,看方法的名字就知道是要找到祖先级别,也就是
//说如果有父工厂,也会去找;这个方法写的太多了,真想说一句卧槽,看的头疼,太绕了,反正记得就是根据requiredType
//类型去spring的当前工厂和父工厂中去找到所有符合条件的bean名字集合,然后返回
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
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;
}
}
}
//下面进行筛选,这个筛选比较复杂isAutowireCandidate这个方法比较重要,也是这里筛选的核心,其核心思想就是你是否
//禁用了自动注入,参数为autowire-candidate,在xml中是autowire-candidate,也是BeanDefinition中的一个属性
//但是这个方法里面不仅仅是检查了这个属性,而且还检查了其他,这个方法里面涉及了几个类
//QualifierAnnotationAutowireCandidateResolver q
//GenericTypeAwareAutowireCandidateResolver g
//SimpleAutowireCandidateResolver s
//我这边简单来说就是s是*父类,g是s的子类,q是g的子类,而isAutowireCandidate方法是q中的,q先调用父类的g的验证
//g又有自己的验证,g也是先调用父类s的验证,然后再调用自己的验证,自己的验证完成过后返回给q,q如果发现父类返回true了才会调用
//自己的验证,其中g是验证泛型的,就是如果有泛型,那么就要验证泛型的类型,而s是简简单单的就验证了BeanDefinition中的isAutowireCandidate属性
//q自己本身的验证包括了Qualifier验证
/**
* 这里还有个方法是isSelfReference,这个是干什么用的呢?
* 这个方法的意思就是说这里是对找到的所有的bean的名字进行筛选,尽量筛选出最符合条件的哪一个,在筛选的过程中把自己先筛掉
* 就是说你可以在bean中这样写:在Userservice中注入了自己 private Userserivce userservice;那么有可能容器中还有
* @Bean生成的一个或者多个UserService对象,在这个时候,如果candidateNames找到了三个,有两个是@Bean生成的bean
* 还有一个就是本身Uservice生成的一个@Compoent扫描到的对象,那么下面的循环就是把自己的@Component找到的对象给筛选出去
* 不加入map
*
*/
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
//符合条件的添加到result中
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
if (result.isEmpty()) {
boolean multiple = indicatesMultipleBeans(requiredType);
// Consider fallback matches if the first pass failed to find anything...
DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
for (String candidate : candidateNames) {
if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
//这里的意思就是说在上面筛选过后,发现没有符合条件的,那么有可能就是说找到的bean中candidateNames只有我一个,
//而且这个bean还是我自己,也就是自己注入自己,虽然这种做法不采用,也没有任何的意思,但是spirng也提供了一个容错的机制
//来满足,所以下面的逻辑就是如果自己注入自己,而在spring容器中也只有自己一个bean对象,那么这里就是做的而自己注入自己
if (result.isEmpty() && !multiple) {
// Consider self references as a final pass...
// but in the case of a dependency collection, not the very same bean itself.
for (String candidate : candidateNames) {
if (isSelfReference(beanName, candidate) &&
(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
isAutowireCandidate(candidate, fallbackDescriptor)) {
addCandidateEntry(result, candidate, descriptor, requiredType);
}
}
}
}
return result;
}
candidateNames 是根据注入的类型去spring的当前容器和父容器中把全部符合条件的beanName都找出来,如果是泛型,可能会把spring容器中的所有beanName都找出来,如果非泛型,那么可能找出来就是byType所代表的类型,找出来过后进行循环,确定符合条件的bean,确定的过程:
1.是否是自动注入的候选者;
2.是否是泛型,如果是泛型,那么就会获取泛型类型在容器中找到的目标类型是否是泛型中指定的类型,如果是返回true,如果不是返回false,返回false就是不能加入byType的列表中;
3.上面两部如果返回true,那么就会判断@Qualifier;
上面三步的代码如下:
QualifierAnnotationAutowireCandidateResolver.isAutowireCandidate
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
//检查父类是否匹配
//1.检查BeanDefinition中的isAutowireCandidate属性是否为true
//2.检查是否有泛型,无泛型直接返回true,有泛型检查descriptor.getDependencyType的泛型类型是否是和BeanDefinition中得到类型一致
boolean match = super.isAutowireCandidate(bdHolder, descriptor);
if (match) {
// 如果上面的检查都通过了,那么这里检查Qualifier,这个和别名有点像,但是不是别名
//它可以设置一个名称,然后根据这个名字来过滤找到的bean,举个例子:
/**
* 有个随机负载均衡器Random,有个轮询的RoudRobin,有个父类Lanbance
* 那么Random和RoudRobin都是Lanbance的子类,那么我们定义了@Qualifier("random")是Random类的
* @Qualifier("roudRobin")的,那么我们只需要切换这个注解的名字就可以随便切换均衡器了,其实用法不止这些,还有其他的
*/
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;
}
GenericTypeAwareAutowireCandidateResolver.isAutowireCandidate
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
//检查父类中的isAutowireCandidate属性是否为true,如果不是直接返回false
if (!super.isAutowireCandidate(bdHolder, descriptor)) {
// If explicitly false, do not proceed with any other checks...
return false;
}
//检查泛型类型
return checkGenericTypeMatch(bdHolder, descriptor);
}
protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
//整个检查是检查你注入的bean类型是否是泛型的,如果是泛型的,那么验证下泛型的类型是否匹配,就是泛型的类型是否是descriptor中的
//getDependecyType()的类型
ResolvableType dependencyType = descriptor.getResolvableType();
if (dependencyType.getType() instanceof Class) {
// No generic type -> we know it's a Class type-match, so no need to check again.
//不是泛型类型,直接返回true,因为不知道class的具体类型,需要再次进行检查
return true;
}
//下的一大堆代码我难得看,直接跳到最后一行,就是和BeanDefinition结合找到targetType类型
//然后看targetType类型是否dependencyType类型,所以直接忽略这一大堆像屎一样的代码
ResolvableType targetType = null;
boolean cacheType = false;
RootBeanDefinition rbd = null;
if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
}
if (rbd != null) {
targetType = rbd.targetType;
if (targetType == null) {
cacheType = true;
// First, check factory method return type, if applicable
targetType = getReturnTypeForFactoryMethod(rbd, descriptor);
if (targetType == null) {
RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd);
if (dbd != null) {
targetType = dbd.targetType;
if (targetType == null) {
targetType = getReturnTypeForFactoryMethod(dbd, descriptor);
}
}
}
}
}
if (targetType == null) {
// Regular case: straight bean instance, with BeanFactory available.
if (this.beanFactory != null) {
Class<?> beanType = this.beanFactory.getType(bdHolder.getBeanName());
if (beanType != null) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanType));
}
}
// Fallback: no BeanFactory set, or no type resolvable through it
// -> best-effort match against the target class if applicable.
if (targetType == null && rbd != null && rbd.hasBeanClass() && rbd.getFactoryMethodName() == null) {
Class<?> beanClass = rbd.getBeanClass();
if (!FactoryBean.class.isAssignableFrom(beanClass)) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanClass));
}
}
}
if (targetType == null) {
return true;
}
if (cacheType) {
rbd.targetType = targetType;
}
if (descriptor.fallbackMatchAllowed() &&
(targetType.hasUnresolvableGenerics() || targetType.resolve() == Properties.class)) {
// Fallback matches allow unresolvable generics, e.g. plain HashMap to Map<String,String>;
// and pragmatically also java.util.Properties to any Map (since despite formally being a
// Map<Object,Object>, java.util.Properties is usually perceived as a Map<String,String>).
return true;
}
// Full check for complex generic type match...
//就是验证找到的targetType是否是dependencyType类型
//dependencyType是我们注入bean的泛型类型,而targetType是前面从spring容器中找到的所有beannName所对应的targetType
return dependencyType.isAssignableFrom(targetType);
}
循环完成过后,符合条件的加入到Map中返回
addCandidateEntry
/**
* Add an entry to the candidate map: a bean instance if available or just the resolved
* type, preventing early bean initialization ahead of primary candidate selection.
* 这里的操作是添加到指定的map对象中,map对象是需要返回的
* 这里的添加看清楚没有,有一个if else,也就是说containsSingleton(candidateName) 如果这个bean已经是在
* spring的单例池中了就返回对象,如果不是就返回一个beanclass对象,因为这个时候我们都还没有确定是你那个bean对象
* 才是我们想要的,所以这个时候不返回对象,而是返回一个class,如果最后确定是一个class,那么再实例化对象也不迟
*/
private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
DependencyDescriptor descriptor, Class<?> requiredType) {
if (descriptor instanceof MultiElementDescriptor) {
Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
if (!(beanInstance instanceof NullBean)) {
candidates.put(candidateName, beanInstance);
}
} else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor &&
((StreamDependencyDescriptor) descriptor).isOrdered())) {
Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
} else {
candidates.put(candidateName, getType(candidateName));
}
}
最后代码逻辑回到方法doResolveDependency的下面片段,matchingBeans 就是找到的符合条件bean对象或者beanclass
//注入的核心方法findAutowireCandidates,这个方法会返回一个Map,也就是说可以有一个,可以有多个,有多个的情况
//就是在spring容器中注入了1个以上的相同的Bean,很简单就是比如一个接口,被几个实现类实现了,那么也会有这种情况
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) {
/**
* 找到有多个bean,这里的处理就比较麻烦一点了
* determineAutowireCandidate确定唯一Bean的三步操作
* 1.检查是否有@Primary注解,如果有就取配置了@Primary注解的bean,如果发现有不止一个bean有@Primary,则报错;如果没有这注解,当没调用
* 2.检查是否有@Priority注解,如果有判断优先级,如果有两个bean的优先级相同则报错;如果没有这注解,当没调用
* 3.如果前两步还没有确定唯一的bean,那么最后一步就是byName,找到唯一的bean
*/
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
} else {
// In case of an optional Collection/Map, silently ignore a non-unique case:
// possibly it was meant to be an empty collection of multiple regular beans
// (before 4.3 in particular when we didn't even look for collection beans).
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
} else {
//代码逻辑到这里,证明上面的找到了唯一的一个Bean,直接处理下返回了
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
//将找到的beanName放入到要返回的map集合中
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
//在前面的方法写过,就是说再找bean的时候,有可能这个bean还没有被实例化也就是class类型,那个时候是没有实例化的,而是添加到map中
//等真正的确定了唯一的bean过后再次实例化,万一找到的不是你想要的那个bean,也就是不是要注入的bean,那么那个时候实例化就是浪费性能
if (instanceCandidate instanceof Class) {
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;
} finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
上面的代码中还有个地方就是isRequired(descriptor),这个表示@AutoWired中的required属性,默认是true,代表如果注入的时候没有找到指定的bean,就报错,如果设置了false,就不会报错。
determineAutowireCandidate
确定唯一的bean的方法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注解,@Priority的值越小,优先级越大determineHighestPriorityCandidate方法的代码简单
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
// Fallback
//最后的byName逻辑
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;
}
protected String determinePrimaryCandidate(Map<String, Object> candidates, Class<?> requiredType) {
/**
* 下的逻辑很简单,candidates是相同类型的bean,定义了primaryBeanName,如果循环到一个Bean的时候
* 是Primary的bean,那么记录下,如果后续循环还有Primary的bean,那么就报错
*/
String primaryBeanName = null;
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateBeanName = entry.getKey();
Object beanInstance = entry.getValue();
if (isPrimary(candidateBeanName, beanInstance)) {
if (primaryBeanName != null) {
boolean candidateLocal = containsBeanDefinition(candidateBeanName);
boolean primaryLocal = containsBeanDefinition(primaryBeanName);
if (candidateLocal && primaryLocal) {
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
"more than one 'primary' bean found among candidates: " + candidates.keySet());
} else if (candidateLocal) {
primaryBeanName = candidateBeanName;
}
} else {
primaryBeanName = candidateBeanName;
}
}
}
return primaryBeanName;
}
/**
* Determine the candidate with the highest priority in the given set of beans.
* <p>Based on {@code @javax.annotation.Priority}. As defined by the related
* {@link org.springframework.core.Ordered} interface, the lowest value has
* the highest priority.
*
* @param candidates a Map of candidate names and candidate instances
* (or candidate classes if not created yet) that match the required type
* @param requiredType the target dependency type to match against
* @return the name of the candidate with the highest priority,
* or {@code null} if none found
* @see #getPriority(Object)
* 优先级的判断,@Priority的值越小,优先级越大
*/
@Nullable
protected String determineHighestPriorityCandidate(Map<String, Object> candidates, Class<?> requiredType) {
String highestPriorityBeanName = null;
Integer highestPriority = null;
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateBeanName = entry.getKey();
Object beanInstance = entry.getValue();
if (beanInstance != null) {
Integer candidatePriority = getPriority(beanInstance);
if (candidatePriority != null) {
if (highestPriorityBeanName != null) {
if (candidatePriority.equals(highestPriority)) {
throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
"Multiple beans found with the same priority ('" + highestPriority +
"') among candidates: " + candidates.keySet());
} else if (candidatePriority < highestPriority) {
highestPriorityBeanName = candidateBeanName;
highestPriority = candidatePriority;
}
} else {
highestPriorityBeanName = candidateBeanName;
highestPriority = candidatePriority;
}
}
}
}
return highestPriorityBeanName;
}
上面的代码逻辑简单来说就是最后确定唯一bean的逻辑,顺序:
aaa@qq.com
aaa@qq.com
3.byName
这三步凡是哪一步已经确定了唯一的bean就直接结束了,不会去后面确定bean了。
大概的流程图如下:
@Qualifier应用场景
@Qualifier的应用场景比较多,在springclound中用的更多,我这边简单记录下应用场景,比如容器中有好几个相同的bean,我们通过上面的源码可以知道,可以通过它来过滤我们想要的bean
@Component
public class UserService {
@Autowired
@Qualifier("orderService")
private OrderService orderService;
public UserService(){
System.out.println("create UserService ...");
}
public void test(){
System.out.println(orderService);
}
}
这样是没有问题的,但是如果我修改了下,这样用@Qualifier(“xxx”),那么spring肯定是找不到的, @Qualifier它不是别名,但是具有别名的功能,还是在多个bean的情况下筛选。下面我以xml的形式来演示下如何使用:
<bean id="user0" class="org.bml10.service.User">
<property name="name" value="user0000"/>
</bean>
<bean id="user1" class="org.bml10.service.User" >
<property name="name" value="user1111"/>
</bean>
<bean name="userService" class="org.bml10.service.UserService" autowire="constructor"/>
user0和user1的类型都是user,
UserService的通过构造方法自动注入。
public class UserService {
private User user;
public UserService(User user11) {
this.user = user11;
}
public void test() {
System.out.println(user.getName());
}
}
现在运行main方法,会启动Spring,会报错:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.luban.entity.User' available: expected single matching bean but found 2: user0,user1
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:223)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1324)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1255)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:939)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:842)
... 15 more
很明显,因为UserService会根据构造方法自动注入,会先根据构造方法参数类型User是找bean,找到了两个,一个user0, 一个user1,然后会根据构造方法参数名user11去进行过滤,但是过滤不出来,最终还是找到了两个,所以没法进行自动注入,所以报错。
怎么办?
要么给其中某个bean设置primary为true,表示找到多个时默认用这个。
或者给某个bean设置autowire-candidate为false,表示这个bean不作为自动注入的候选者。
还有一种就是@Qualifier, 首先,给两个bean分别定义一个qualifier:
<bean id="user0" class="org.bml10.service.User" autowire-candidate="false">
<property name="name" value="user0000"/>
<qualifier value="user00"/>
</bean>
<bean id="user1" class="org.bml10.service.User" >
<property name="name" value="user1111"/>
<qualifier value="user11"/>
</bean>
同时在UserService中使用@Qualifier注解:
public UserService(@Qualifier("user00") User user11) {
this.user = user11;
}
还需要做一件事情,因为我们是使用的XML的方式使用Spring,所以还需要添加:
<context:annotation-config/>
这样@Qualifier注解才会生效,这样配置之后UserService中的user属性就会被赋值为@Qualifier(“user00”)所对应的user0这个bean。
@Qualifier注解是在使用端去指定想要使用的bean,而autowire-candidate和primary是在配置端。
@Qualifier的另外一种用法
定义一个:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
修改bean的定义
<bean id="user0" class="org.bml10.service.User">
<property name="name" value="user0000"/>
<qualifier type="org.bml10.annotation.Genre" value="user00"/>
</bean>
<bean id="user1" class="org.bml10.service.User" >
<property name="name" value="user1111"/>
<qualifier type="org.bml10.annotation.Genre" value="user11"/>
</bean>
UserService中使用Genre注解:
public class UserService {
private User user;
public UserService(@Genre("user00") User user11) {
this.user = user11;
}
public void test() {
System.out.println(user.getName());
}
}
这种用法相当于自定义@Qualifier注解,使得可以处理更多的情况。
同样,还可以这么做,定义两个注解:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("random")
public @interface Random {
}
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("roundRobin")
public @interface RoundRobin {
}
注意,是在自定义的Qualifier注解上指定了名字,这样就是可以直接使用自定义注解,而不用指定名字了。比如先修改bean的定义:
<bean id="user0" class="org.bml10.service.User">
<property name="name" value="user0000"/>
<qualifier type="org.bml10.annotation.Random"/>
</bean>
<bean id="user1" class="org.bml10.service.User" >
<property name="name" value="user1111"/>
<qualifier type="org.bml10.annotation.RoundRobin"/>
</bean>
UserService中就可以这么用:
public UserService(@RoundRobin User user11) {
this.user = user11;
}
@Qualifier的使用场景在spring cloud中有个经典就是负载均衡器,@Random @RoudRobin分表是属于RandomService和RoudRobinService,然后父类Lanblance,最后这样定义:
@Random
private LoadBlance loadBlance;
@RoudRobin
private LoadBlance loadBlance;
第一个就代表是随机分配策略
第二个就代表的是轮休策略
所以spring的@Qualifier应用场景非常强大,要好好理解和使用