Spring Boot 自动配置 源码分析
Spring Boot 最大的特点(亮点)就是自动配置 AutoConfiguration
下面,先说一下 @EnableAutoConfiguration ,然后再看源代码,到底自动配置是怎么配置的
1. @EnableAutoConfiguration

@SpringBootApplication是一个复合注解,本节我们重点关注 @EnableAutoConfiguration

自动配置类是常规的Spring @Configuration bean。它们使用SpringFactoriesLoader机制定位。通常,自动配置bean是@Conditional Bean(最经常使用@ConditionalOnClass和@ConditionalOnMissingBean注解)
在@EnableAutoConfiguration注解上有一个@Import注解
@Import这个注解表明要导入的一个或多个组件类,通常是@Configuration类。
@Import注解提供与Spring XML中的<import />元素等效的功能。允许导入@Configuration类,ImportSelector和ImportBeanDefinitionRegistrar实现以及常规组件类。



根据导入的@Configuration类的AnnotationMetadata,返回AutoConfigurationImportSelector.AutoConfigurationEntry。
方法的参数AnnotationMetadata代表配置类上的注解元数据,方法的返回值是应该被导入的自动配置类
首先,获取配置类上的注解的属性

又是熟悉的方法:SpringFactoriesLoader.loadFactoryNames()

在所有 META-INF/spring.factories 文件中查找 org.springframework.boot.autoconfigure.EnableAutoConfiguration

然后,去重

然后,再排除一些,根据注解属性中明确指定的exclude

删除所有需要排除的
然后,过滤掉一些不需要的



根据对所有的需要自动配置的类应用那三个过滤器
最终剩下的就是真正需要导入的,或者说真正需要自动配置的
在众多需要自动配置的类中,我们挑一个熟悉的来看一下,就挑org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration

看这注解,当classpath中有RedisOperations时才会自动配置该类,当有RedisConnectionFactory且没有redisTemplate时才会创建一个redisTemplate,同理,有RedisConnectionFactory且没有stringRedisTemplate时才会创建一个stringRedisTemplate
回顾一下
1、在所有 META-INF/spring.factories 文件中查找 org.springframework.boot.autoconfigure.EnableAutoConfiguration,返回一个List<String>
2、对上一步返回的List去重
3、根据注解exclude属性排除List中的一些元素
4、根据AutoConfigurationImportFilter过滤掉一些不需要自动配置的元素
5、讲过以上四步,List中剩下的元素就是最终需要自动配置的元素(类)
至此,只是筛选出了哪些类需要自动配置,但还没有真正装配(实例化),真正实例化Bean是在Spring Boot启动时刷新ApplicationContext时做的
注解只是个标记,是给反射用的,有注解必然有处理它的类
接下来,分析源码,看看究竟什么时候开始真正自动装配
2. Spring Boot 自动配置源码分析

又来到了熟悉的SpringApplication.run()方法这里,这一次,重点看其中的三步:createApplicationContext()、prepareContext() 和 refreshContext()
首先看createApplicationContext()

看看AnnotationConfigServletWebServerApplicationContext有多复杂


创建了两个BeanDefinition分别是AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner,它们都是用来查找并加载Bean定义的,只是方式不同而已
下面重点看一下AnnotatedBeanDefinitionReader




(PS:其实,这里注册了很多BeanPostProcessor,有处理Autowired的AutowiredAnnotationBeanPostProcessor,由于本节主要讲自动配置的,所以我们重点关注ConfigurationClassPostProcessor)
先做个笔记:
1、ApplicationContext 是 AnnotationConfigServletWebServerApplicationContext
2、构造了一个AnnotatedBeanDefinitionReader,大家要明白BeanDefinitionReader是用来加载Bean定义的
3、把这个AnnotatedBeanDefinitionReader注册(关联)到该ApplicationContext
4、在构造AnnotatedBeanDefinitionReader的时候注册了很多Processors
可见AnnotatedBeanDefinitionReader真的是相当相当重要,而重中之重是org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors()
我们单独把这段拿出来再看一下


如果没有这个BeanDefinition的时候就添加一个
CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME 对应的是 ConfigurationClassPostProcessor
先记住这一点,后面会用到
接下来,看prepareContext()



重点看SharedMetadataReaderFactoryContextInitializer

好,记住这一点,此处添加了一个BeanFactoryPostProcessor,它是一个CachingMetadataReaderFactoryPostProcessor
最后,再来看refreshContext()
调用是Spring的refresh()


重点看 invokeBeanFactoryPostProcessors 调用所有已注册的BeanPostProcessor


这里,调用getBeanFactoryPostProcessors()返回的BeanFactoryPostProcessors中有CachingMetadataReaderFactoryPostProcessor
好,记住这一点
继续看PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()

好,看CachingMetadataReaderFactoryPostProcessor#postProcessBeanDefinitionRegistry()

又看到了熟悉的AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME,我们知道它是ConfigurationClassPostProcessor
回到PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()继续往下看

得到ConfigurationClassPostProcessor
接下来,调用postProcessBeanDefinitionRegistry



方法太长,就不截全图了,总之就是找到配置类,开始解析配置类了,只看重点
到这里终于和我们前面讲的@EnableAutoConfiguration自动配置开始沾点儿边了


下面,重点来了,核心中的核心

终于写完了,累死我了
Spring Boot 自动配置 源码分析的更多相关文章
- Spring Boot自动配置源码解析(基于Spring Boot 2.0.2.RELEASE)
在Spring Boot官方介绍中,首一段话是这样的(如下图).我们可以大概了解到其所表达的含义:我们可以利用Spring Boot写很少的配置来创建一个非常方便的基于Spring整合第三方类库的单体 ...
- spring boot 2.0 源码分析(一)
在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...
- spring boot 2.0 源码分析(四)
在上一章的源码分析里,我们知道了spring boot 2.0中的环境是如何区分普通环境和web环境的,以及如何准备运行时环境和应用上下文的,今天我们继续分析一下run函数接下来又做了那些事情.先把r ...
- spring boot 2.0 源码分析(二)
在上一章学习了spring boot 2.0启动的大概流程以后,今天我们来深挖一下SpringApplication实例变量的run函数. 先把这段run函数的代码贴出来: /** * Run the ...
- spring boot 2.0 源码分析(三)
通过上一章的源码分析,我们知道了spring boot里面的listeners到底是什么(META-INF/spring.factories定义的资源的实例),以及它是创建和启动的,今天我们继续深入分 ...
- spring boot 2.0 源码分析(五)
在上一篇文章中我们详细分析了spring boot是如何准备上下文环境的,今天我们来看一下run函数剩余的内容.还是先把run函数贴出来: /** * Run the Spring applicati ...
- Springboot 系列(三)Spring Boot 自动配置原理
注意:本 Spring Boot 系列文章基于 Spring Boot 版本 v2.1.1.RELEASE 进行学习分析,版本不同可能会有细微差别. 前言 关于配置文件可以配置的内容,在 Spring ...
- SpringBoot自动配置源码调试
之前对SpringBoot的自动配置原理进行了较为详细的介绍(https://www.cnblogs.com/stm32stm32/p/10560933.html),接下来就对自动配置进行源码调试,探 ...
- Spring Boot自动配置与Spring 条件化配置
SpringBoot自动配置 SpringBoot的自动配置是一个运行时(应用程序启动时)的过程,简化开发时间,无需浪费时间讨论具体的Spring配置,只需考虑如何利用SpringBoot的自动配置即 ...
随机推荐
- Linux-用户/用户组身份提权
sudo 身份提权(更安全) su命令在切换用户身份时,如果每个普通用户都能拿到root用户的密码,当其中某个用户不小心泄漏了root的密码,那系统会变得非常不安全. 为了改进这个问题,从而产生了su ...
- 2021-2-17:Java HashMap 的中 key 的哈希值是如何计算的,为何这么计算?
首先,我们知道 HashMap 的底层实现是开放地址法 + 链地址法的方式来实现. 即数组 + 链表的实现方式,通过计算哈希值,找到数组对应的位置,如果已存在元素,就加到这个位置的链表上.在 Java ...
- Spring Cloud Alibaba+Nacos搭建微服务架构
1. Spring Cloud Alibaba 简介 Spring Cloud Alibaba是阿里巴巴为分布式应用提供的一站式解决方案,能够更方便快捷地搭建分布式平台,nacos拥有着替换eu ...
- HTML5 Animation Creator | Hype 官方文档
HTML5 Animation Creator | Hype 官方文档 HTML5 Animation Build 7个 专业术语 场景,元素,属性,关键帧,动画,时间线,操作 Hype 官方文档 h ...
- cnblogs & 502 Bad Gateway
cnblogs & 502 Bad Gateway 博客园 502 Bad Gateway 服务器发生了一些错误,请联系 contact@cnblogs.com 可以查看,不可以编辑 HTTP ...
- Contributor License Agreement
Contributor License Agreement CLA https://cla.js.foundation/lodash/lodash?pullRequest=4756 https://g ...
- dart 匹配基本map
var map_start = RegExp(r'^\s*\{\s*'); var map_end = RegExp(r'^\}\s*(,)?\s*'); var hasComma = true; M ...
- 人物传记STEPHEN LITAN:去中心化存储是Web3.0生态重要组成
近期,NGK.IO的开发团队首席技术官STEPHEN LITAN分享了自己对去中心化储存的观点,以下为分享内容. 目前的存储方式主要是集中式存储,随着数据规模和复杂度的迅速增加,集中存储的数据对于系统 ...
- 「NGK每日快讯」12.18日NGK公链第45期官方快讯!
- ROS 安装完成后运行小乌龟示例程序
安装ROS成功后,在Beginner Tutorials中有一个简单的示例程序. 在Terminal中运行以下命令: $ roscore 新开一个terminal,运行以下命令,打开小乌龟窗口: $ ...