原创001 | 搭上SpringBoot自动注入源码分析专车
前言
如果这是你第二次看到师长的文章,说明你在觊觎我的美色!O(∩_∩)O哈哈~
点赞+关注再看,养成习惯
没别的意思,就是需要你的窥屏_
本系列为SpringBoot深度源码专车系列,第一篇发车!
专车介绍
该趟专车是开往Spring Boot自动注入原理源码分析的专车
专车问题
- Spring Boot何时注入@Autowired标注的属性?
- 如果注入类型的Bean存在多个Spring Boot是如何处理的?
专车示例
- 定义接口
public interface PersonService {
String hello(String name);
}
- 定义接口的一个实现
@Service(value = "studentService")
public class StudentServiceImpl implements PersonService {
@Override
public String hello(String name) {
return "[student service] hello " + name;
}
}
- 定义接口的另一个实现
@Service(value = "teacherService")
public class TeacherServiceImpl implements PersonService {
@Override
public String hello(String name) {
return "[teacher service] hello " + name;
}
}
- 定义控制器
@RestController
public class TestController {
@Autowired
private PersonService studentService;
@Autowired
private PersonService teacherService;
@GetMapping("/hello")
public String hello(@RequestParam(name = "name") String name) {
return studentService.hello(name) + "=======>" + teacherService.hello(name);
}
}
以上示例代码很简单,创建了一个接口,接口有两个实现类,然后在控制器中注入实现类,从而完成业务方法的调用。接下来我们就开始对源码进行分析
专车分析
在分析代码之前我们先回忆一下操作对象的步骤:
- 首先我们会实例化一个对象
- 然后调用对象的set方法来设置对象的属性
有了上面的基础知识,接下来就开始揭秘旅程
寻找入口
在分析源码的时候最关键的一步就是寻找程序的入口,有了入口我们就成功了一半,那么如何寻找程序的入口?针对此处的源码分析,我们可以在TestController类上打一个断点,然后查看调用链
基于调用链路,我们看到有一个doCreateBean方法,该方法就是用来创建bean的,也就是我们上面提到的实例化对象部分
实例化Bean
AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 创建bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
// ...省略部分代码
// Initialize the bean instance.
Object exposedObject = bean;
try {
// 填充bean,也就是我们上面提到的调用对象的set方法设置对象属性
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
// ...省略部分代码
return exposedObject;
}
填充bean
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// ...省略代码
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
// 遍历所有的后置处理器
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 通过断点分析我们可以得知此处调用的是AutowiredAnnotationBeanPostProcessor#postProcessProperties
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
处理属性
AutowiredAnnotationBeanPostProcessor#postProcessProperties
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 查找当前bean需要注入的元数据信息,以TestController为例,那么需要注入的就是studentService和teacherService两个属性
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 注入属性
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
注入属性 AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 获取属性,此处的属性就是studentService
Field field = (Field) this.member;
// 属性对应的value
Object value;
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
// 解析属性依赖
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
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) {
ReflectionUtils.makeAccessible(field);
// 给属性设置值,完成注入功能
field.set(bean, value);
}
}
解析属性依赖 DefaultListableBeanFactory#resolveDependency
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;
}
}
解析属性依赖 DefaultListableBeanFactory#doResolveDependency
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
// ...省略代码
// 解析多个Bean,比如Array、List、Map类型,有兴趣可以自己查看分析
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
// 根据类型获取候选对象,针对studentService而言,该属性的类型为PersonService
// PersonService有2个实现类,StudentServiceImpl和TeacherServiceImpl
// 所以此处获取结果为StudentServiceImpl对象和TeacherServiceImpl对象
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;
// 重点处理,如果存在多个匹配的bean
if (matchingBeans.size() > 1) {
// 从已经匹配的bean中选择一个符合的bean
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
// 如果bean必须注入或者存在多个匹配的bean,则抛出异常
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;
}
}
// 根据bean名称获取对应的示例
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();
}
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
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);
}
}
此处主要根据类型获取所有匹配的bean,如果匹配的bean有多个,那么最后会选择一个符合条件的bean名称,然后将对应的bena实例返回,调用set方法进行进行注入,到此注入的原理本该结束了。但是还是要分析一下Spring Boot是如何选择出符合条件的bean?
选择符合条件的bean DefaultListableBeanFactory#determineAutowireCandidate
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
// 如果bean对应的primary属性为true,则返回bean对应的名称
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
// 如果候选bean使用javax.annotation.Priority标注,返回高优先级bean对应的名称
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
// Fallback
// 如果匹配bean的名称和需要注入的属性名称一致,则返回匹配bean的名称
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名称总结:
- 依据Bean的primary属性
- 依据javax.annotation.Priority
- 依据注入属性的名称
专车总结
- Bean实例化完成后,填充Bean
- 调用AutowiredAnnotationBeanPostProcessor#postProcessProperties处理属性
- 获取所有需要注入的属性
- 根据注入属性的类型从IOC容器中查找匹配实例
- 如果匹配实例存在多个,根据primary属性--->javax.annotation.Priority注解--->注入属性名称依次过滤,返回符合条件的Bean名称
- 过滤之后,存在一个符合条件的Bean名称,则返回对应的实例,否则抛出异常
专车回顾
回顾一下开头的2个问题:
- Spring Boot何时注入@Autowired标注的属性?
- 如果注入类型的Bean存在多个Spring Boot是如何处理的?
第一个问题:是在Bean实例化后,填充Bean的时候注入@Autowired标注的属性
第二个问题:如果存在多个类型的Bean,会根据primary--->javax.annotation.Priority--->名称依次过滤,得到最终匹配的bean名称
原创001 | 搭上SpringBoot自动注入源码分析专车的更多相关文章
- SpringBoot自动装配-源码分析
1. 简介 通过源码探究SpringBoot的自动装配功能. 2. 核心代码 2.1 启动类 我们都知道SpringBoot项目创建好后,会自动生成一个当前模块的启动类.如下: import org. ...
- 【原创】005 | 搭上SpringBoot请求处理源码分析专车
前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍 该趟专车是开往Spring Boot请求处理源码分析专车,主要用来分析S ...
- SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的
系列文章目录和关于我 一丶什么是SpringBoot自动装配 SpringBoot通过SPI的机制,在我们程序员引入一些starter之后,扫描外部引用 jar 包中的META-INF/spring. ...
- SpringBoot自动配置源码调试
之前对SpringBoot的自动配置原理进行了较为详细的介绍(https://www.cnblogs.com/stm32stm32/p/10560933.html),接下来就对自动配置进行源码调试,探 ...
- springboot整合mybatis源码分析
springboot整合mybatis源码分析 本文主要讲述mybatis在springboot中是如何被加载执行的,由于涉及的内容会比较多,所以这次只会对调用关系及关键代码点进行讲解,为了避免文章太 ...
- Spring Boot 自动配置 源码分析
Spring Boot 最大的特点(亮点)就是自动配置 AutoConfiguration 下面,先说一下 @EnableAutoConfiguration ,然后再看源代码,到底自动配置是怎么配置的 ...
- 【原创】002 | 搭上SpringBoot事务源码分析专车
前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍** 该趟专车是开往Spring Boot事务源码分析的专车 专车问题 为 ...
- tomcat源码--springboot整合tomcat源码分析
1.测试代码,一个简单的springboot web项目:地址:https://gitee.com/yangxioahui/demo_mybatis.git 一:tomcat的主要架构:1.如果我们下 ...
- HashMap源码分析(史上最详细的源码分析)
HashMap简介 HashMap是开发中使用频率最高的用于映射(键值对 key value)处理的数据结构,我们经常把hashMap数据结构叫做散列链表: ObjectI entry<Key, ...
随机推荐
- vue2.0 与 vue3.0 配置的区别
提示:要了解vue2.0与vue3.0区别,首先你要熟悉vue2.0 从最明显最简单的开始 项目目录结构 可以明显的看出来,vue2.0与3.0在目录结构方面,有明显的不同(vue3.0我是安装了cs ...
- 作为一名程序员,你真正了解CDN技术吗?
本文导读: 物流仓库配送如何加速 静态资源文件部署方式 静态资源加速之CDN技术 解析过程中的名词解释 最后的总结 1.物流仓库配送如何加速 我们还是从生活中购物的例子来展开. 将时光倒回到几年前,在 ...
- Web for pentester_writeup之Commands injection篇
Web for pentester_writeup之Commands injection篇 Commands injection(命令行注入) 代码注入和命令行注入有什么区别呢,代码注入涉及比较广泛, ...
- Docker的centos7容器中如何安装mongodb
下载安装包: wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.2.12.tgz 解压安装包 tar -zxvf mongodb ...
- python语言线程标准库threading.local源码解读
本段源码可以学习的地方: 1. 考虑到效率问题,可以通过上下文的机制,在属性被访问的时候临时构建: 2. 可以重写一些魔术方法,比如 __new__ 方法,在调用 object.__new__(cls ...
- python——自行实现sorted函数
仿照内建函数sorted,自行实现一个sort函数,能够为列表进行排序 看下面实例的前提是掌握 0.掌握sorted函数的用法.1.函数基本概念.2.函数是如何传参的.3.掌握三元表达式.4.掌握内建 ...
- 数据结构之队列and栈总结分析
一.前言: 数据结构中队列和栈也是常见的两个数据结构,队列和栈在实际使用场景上也是相辅相成的,下面简单总结一下,如有不对之处,多多指点交流,谢谢. 二.队列简介 队列顾名思义就是排队的意思,根据我们的 ...
- springcloud微服务基于redis集群的单点登录
springcloud微服务基于redis集群的单点登录 yls 2019-9-23 简介 本文介绍微服务架构中如何实现单点登录功能 创建三个服务: 操作redis集群的服务,用于多个服务之间共享数据 ...
- hdu 1263 水果 (嵌套 map)
水果Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submissio ...
- .NET Core 3 WPF MVVM框架 Prism系列之数据绑定
一.安装Prism 1.使用程序包管理控制台 Install-Package Prism.Unity -Version 7.2.0.1367 也可以去掉‘-Version 7.2.0.1367’获取最 ...