Spring源码分析之依赖注入(二)
一、前言
二、方法的注入
我们上一篇文章最后讲到了方法的注入, 保留了一个重要方法没有进行讲解, 具体如下图所示
我们点进这个方法, 具体如下图所示, 接下来我就开始为大家讲解这个方法具体的内容
下图红框部分是获取当前注入方法的入参, 因为一个方法可能有多个入参, 所以用数组接收,
然后去遍历这个数组, 分别的根据这几个参数的信息分别去 BeanFactory中找到对应的 bean
在这个遍历循环的过程当中有一个方法是很重要的, 接下来我带大家看一下这个方法的内部实现
在上图这个方法中, 入参的 DependencyDescriptor descriptor需要注意一下, 因为这个参数可以是字段或者方法参数, 但是我们去获取这个参数的信息, 只会获取其名字和类型
该方法简单流程说明:
- 获取方法入参名字
- 通过java反射获取的, 该方法只做了这一件事
- 判断参数的类型(大多数情况下直接进入 else中)
- else
- 判断方法或属性上有没有使用 @Lazy注解
三、如果有 @Lazy注解
如果有 @Lazy注解则会进入以下方法
在 IsLazy方法中, 他会先去判断你的属性有没有 @Lazy注解, 再去判断方法的参数有没有使用 @Lazy注解
假设存在 @Lazy注解, 那么通过三元运算符会进入到 buildLazyResolutionProxy方法中, 这个方法内部主要是和 AOP相关, 后续我们会出专门的文章进行讲解, 目前暂时知道它会产生一个代理对象就可以了
简单流程说明:
- 最开始为属性赋值, 但是因为属性是被 @Lazy注解修饰的, 所以被赋的值是一个 代理对象
- 当真正的去执行这个 代理对象的某个方法的时候
- 才会根据 这个代理对象字段的信息去 BeanFactory中找对应的 bean对象
- 再去执行 bean对象对应的方法
四、如果没有 @Lazy注解
我们回到之前的方法中, 如果没有 @Lazy注解, 那么就会进入到下面的这个方法中
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 {
// 如果当前descriptor之前做过依赖注入了,则可以直接取shortcut了,相当于缓存
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
Class<?> type = descriptor.getDependencyType();
// 获取@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);
// 解析Spring表达式(#{})
value = evaluateBeanDefinitionString(strVal, bd);
}
// 将value转化为descriptor所对应的类型
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()));
}
}
// 如果descriptor所对应的类型是数组、Map这些,就将descriptor对应的类型所匹配的所有bean方法,不用进一步做筛选了
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
// 找到所有Bean,key是beanName, value有可能是bean对象,有可能是beanClass
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
// required为true,抛异常
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
if (matchingBeans.size() > 1) {
// 根据类型找到了多个Bean,进一步筛选出某一个, @Primary-->优先级最高--->name
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 {
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
}
// 记录匹配过的beanName
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
// 有可能筛选出来的是某个bean的类型,此处就进行实例化,调用getBean
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);
}
}
resolveMultipleBeans方法
我们看一下 resolveMultipleBeans详情
可以看到, 他会根据你属性注入的类型不同来进行不同的操作, 会去直接匹配对应的 bean方法,不在进行进一步的筛选了
例如当我们属性注入的类型为 Map的时候, 它会强制要求key为String类型, 并去寻找相应的 bean
具体的如下所示, 他会根据 bean的类型去找 bean的名字
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
new MultiElementDescriptor(descriptor));
然后去判断 如果没有找到 Bean则抛出异常
核心的findAutowireCandidates方法
流程信息
- 找出BeanFactory中类型为type的所有的Bean的名字,注意是名字,而不是Bean对象,因为我们可以根据BeanDefinition就能判断和当前type是不是匹配,不用生成Bean对象
- 把resolvableDependencies中key为type的对象找出来并添加到result中
- 遍历根据type找出的beanName,判断当前beanName对应的Bean是不是能够被自动注入
- 先判断beanName对应的BeanDefinition中的autowireCandidate属性,如果为false,表示不能用来进行自动注入,如果为true则继续进行判断
- 判断当前type是不是泛型,如果是泛型是会把容器中所有的beanName找出来的,如果是这种情况,那么在这一步中就要获取到泛型的真正类型,然后进行匹配,如果当前beanName和当前泛型对应的真实类型匹配,那么则继续判断
- 如果当前DependencyDescriptor上存在@Qualifier注解,那么则要判断当前beanName上是否定义了Qualifier,并且是否和当前DependencyDescriptor上的Qualifier相等,相等则匹配
- 经过上述验证之后,当前beanName才能成为一个可注入的,添加到result中
源码讲解
我们进入这个方法, 里面的注释是比较清楚的, 接下来主要是针对一些方法的讲解
beanNamesForTypeIncludingAncestors方法
- 先根据自己的类型去 BeanFactory中找 bean的名字
- 如果有 父BeanFactory, 就去 父BeanFactory中找
- 然后将两个集合合并起来返回
怎么根据 Bean的类型去找
我们进入 getBeanNamesForType方法, 具体如下图所示
doGetBeanNamesForType方法
这个方法太长了, 而且我看着真的蒙, 简单讲一下流程, 感兴趣的话大家自己看一下:
- 遍历 BeanDefinition
- 判断bean名字前有没有&符号, 有的话去处理掉
- 拿取 BeanDefinition的属性判断当前的是否匹配
返回
我们回到本方法的开头, 只要知道这个方法是从 BeanFactory中找出和 requiredType所匹配的 beanName, 仅仅是 beanName, 这些 bean不一定经过了实例化, 只有到最终确定某个 Bean了, 如果这个Bean还没有实例化才会进行实例化
resolvableDependencies
在 Spring启动的时候就会忘这个 map中存东西,
这个 map具体的存储过程如下图所示: 后续讲Spring启动源码的时候会讲
Bean对象加入到集合
然后去遍历这个 map, 获取到 key也就是 Bean类型, 判断有没有相同的 Bean类型, 如果有就把对应的对象加入到我们的返回数组中
遍历判断是不是自己注入自己
- 如果不是自己注入自己,则判断该candidate到底能不能用来进行自动注入
- 注入非自己
- 如果有多个candidate 最终将匹配成功 candidate加入到 result
- 因为有多个, 且result不为空
- 所以直接返回 result
result.isEmpty()
- 假设有多个 candidate, 且都不匹配, result == null
- 则会去判断需要的类型是不是 Map, 数组之类的
- 中间那个for循环用的很少 , 就先不看了
- 如果匹配的是自己, 就将自己添加到result中
返回
然后去判断 如果没有找到 Bean则抛出异常
如果找到了多个 Bean
如图所示, 如果根据 beanName找到了多个 Bean那么该 if判断则为 true
determineAutowireCandidate方法
在这个方法中, 我们先去获取他的类型, 获取类型之后执行 determinePrimaryCandidate方法, 下图是这个方法的详情
上图中的方法经历了以下几个步骤:
- 遍历 map获取对应的 beanName和 ( bean对象或 Class类)
- isPrimary方法, 判断这多个 Bean有没有否实现了 @isPrimary注解
- 如果都实现了 @isPrimary注解则抛出异常
- 如果只有一个实现了 @isPrimary注解则使用这个 Bean
方法结束, 返回上一层 determineAutowireCandidate方法
途中红框部分的方法讲解完毕, 接下来它去寻找了我们的返回值, 如果都没有实现 @Primary注解, 那么继续往下走
determineHighestPriorityCandidate方法
方法流程如下:
- 遍历map
- 取当前某个Bean的优先级是什么
- 获取 @Priority注解, 方法在下图红框位置
@Priority 该注解不能使用在方法上, 只能使用在类上面
@Order在依赖注入中是没有去使用判断的
方法结束, 返回上一层 determineAutowireCandidate方法
如上图所示, 我们继续往下走, 最后才是根据名字去判断 bean对象的
通过源码可以看到 @Resource 和 @Autowired的区别 如果有多个同名beanName, 那么@Autowired 会先去寻找有没有哪个 bean是有 @Primary注解的, 如果都没有, 则去寻找有没有哪个类上是有 @Priority优先级注解的, 如果都没有则会去按照名称查找 这个面试题在刚毕业的时候也是经常被问到的, 以前只知道答案, 这次通过源码对其的解答也更加清晰明了了
返回
接下来返回到下面红框的位置, 继续玩下走
如果没有找到对象, required == true抛出异常, 如果不为 true则返回 null, 然后赋值
如果只找到了一个 Bean
那么直接为之前定义的 autowiredBeanName 和 instanceCandidate进行赋值
如果找到了Bean
如果找到了Bean , 则进行记录
判断 instanceCandidate是不是一个类
如果是一个类, 那么获取这个类的 bean
去判断这个类型是不是 NullBean
NullBean的定义代码如下所示, 当以下面的方式定义一个 bean的时候, 这个bean存在 map中的 key为 JuejinService, value为 NullBean
相关判断代码如下所示
最后返回 over
最后红框判断的是期望类型和实际获取到的类型是否一致问题, 就不详细解释了
Spring专栏历史文章
本文内容到此结束了
如有收获欢迎点赞👍收藏💖关注✔️,您的鼓励是我最大的动力。
如有错误❌疑问💬欢迎各位大佬指出。
我是 宁轩 , 我们下次再见
转载自:https://juejin.cn/post/7137332166647611406