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-输出/输入重定向
目录 重定向的分类 输出重定向 将标准输出重定向到文件 将标准输出追加重定向到文件 将错误输出重定向到文件 将标准输出和错误输出都重定向到文件 将错误输出重定向到黑洞文件 输入重定向 重定向的分类 名 ...
- Kubernets二进制安装(4)之Docker安装
注意:需要安装Docker的机器为mfyxw30.mfyxw40.mfyxw50 集群规划 主机名 角色 IP地址 mfyxw30.mfyxw.com Docker 192.168.80.30 mfy ...
- 探究为什么FreeRTOS 有些API不能在中断服务函数中调用,转而需要调用带ISR的版本
用了好久的FreeRTOS以前只是知道,如果在中断服务程序中调用某一些FreeRTOS的API函数时需要注意,如果有ISR版本的一定要调用末尾带ISR的函数,并且中断服务程序要调用freeRTOS的A ...
- Building an IMAP Email Client with PHP
1 Building an IMAP Email Client with PHP http://www.toptal.com/php/building-an-imap-email-client-wit ...
- 树莓派 4B 入门教程
树莓派 4B 入门教程 Raspberry Pi, Raspberry Pi 3B, Raspberry Pi 4B 树莓派 4B 入门手册 PDF Raspberry Pi Beginners Gu ...
- javascript disable scroll event
javascript disable scroll event Document: scroll event https://developer.mozilla.org/en-US/docs/Web/ ...
- nasm astrcmp函数 x86
xxx.asm: %define p1 ebp+8 %define p2 ebp+12 %define p3 ebp+16 section .text global dllmain export as ...
- NGK公链账本技术浅析
NGK公链账本是一个去中心化加密账本,运行在分布式网络上.分布式账本是区块链技术中最重要的组成部分之一.NGK作为公链资产,在公链中起到桥梁作用,可以促进其他资产(法币.数字资产.股权以及实物资产)交 ...
- Redis 对过期数据的处理
Redis 对过期数据的处理 在 redis 中,对于已经过期的数据,Redis 采用两种策略来处理这些数据,分别是惰性删除和定期删除 惰性删除 惰性删除不会去主动删除数据,而是在访问数据的时候,再检 ...
- Flex实现复杂布局
巧妙利用边框(或者背景颜色)来进行布局. 填上内容和图片(由设计提供切图). 去掉边框. css .business-content-1 { display: flex; /* 弹性布局 */ mar ...