我的Spring Boot学习记录(一):自动配置的大致调用过程
1. 背景
Spring Boot通过包管理工具引入starter包就可以轻松使用,省去了配置的繁琐工作,这里简要的通过个人的理解说下Spring Boot启动过程中如何去自动加载配置。
本文中使用的Spring Boot版本为2.0.0.RELEASE
这里主要是说自动配置大致调用流程,其他暂不做分析
2. 主要内容
2.1. spring.factories
首先,需要了解一件事,首先得知道有这么一件事,而自动配置这一件事得从META-INF/spring.factories
说起。其本质类似properties
文件,一种key-value型的文件。
在Spring Boot的官方文档中,Creating Your Own Auto-configuration里面,它可以扫描加载META-INF/spring.factories
中的EnableAutoConfiguration
为key的配置类。引入一个依赖,例如:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这个starter只是引入了其必须的依赖,没有做任何工作,最主要的是有个依赖为spring-boot-starter
,这里包含了一个spring.factories,里面就有各种自动配置的EnableAutoConfiguration
2.2. 怎么加载EnableAutoConfiguration
2.2.1 SpringApplication.run
此函数是一个Spring Boot项目的入口,这里与@SpringBootApplication
有很大的关联。
Spring Boot项目一般的启动代码如下:
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
//主要提供了一个静态函数run来调用
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
再看run函数
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
return run(new Class<?>[] { primarySource }, args); //
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
// 最终被调用的run函数
public ConfigurableApplicationContext run(String... args) {
//......
context = createApplicationContext();
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
//......
return context;
}
以上中,最终是调用了org.springframework.boot.SpringApplication#run(java.lang.String...)
方法,此方法主要是准备了Environment和ApplicationContext。而ApplicationContext就是Spring项目核心的东西,那与自动配置又有什么关系,这里就需要回去看下@SpringBootApplication
注解
2.2.2 @SpringBootApplication
//....
@EnableAutoConfiguration
public @interface SpringBootApplication {
//.....
}
//在上面@SpringBootApplication的注解代码中,有个@EnableAutoConfiguration
//.....
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//...
}
上面为SpringBootApplication和EnableAutoConfiguration注解的部分代码,其中,在EnableAutoConfiguration注解中又有@Import(AutoConfigurationImportSelector.class)
,这里的@Import
是Spring context的内容,与后面的内容中org.springframework.context.annotation.ConfigurationClassParser
类有关联,目前要知道其主要功能就是将AutoConfigurationImportSelector
加载至上下文中。在了解AutoConfigurationImportSelector
源码之前,我们需要先知道SpringFactoriesLoader
,这就是一个META-INF/spring.factories
文件加载器。 其源码可看org.springframework.core.io.support.SpringFactoriesLoader
现在,我们看至AutoConfigurationImportSelector
的源码:
/**
* {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration
* auto-configuration}. This class can also be subclassed if a custom variant of
* {@link EnableAutoConfiguration @EnableAutoConfiguration}. is needed.
*
* 主要的意思为:DeferredImportSelector能够去处理 EnableAutoConfiguration自动配置类Import工作
*/
public class AutoConfigurationImportSelector
implements DeferredImportSelector,... {
/**
* 返回一个需要被加载至Spring上下文的的类名数组
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//....
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
//....
return StringUtils.toStringArray(configurations);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
//通过SpringFactoriesLoader加载出所有的EnableAutoConfiguration类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
return configurations;
}
/**
* 返回了一个让SpringFactoriesLoader加载的Class,就是EnableAutoConfiguration
*/
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
}
那么究竟是谁去调用了org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
并加载了那些EnableAutoConfiguration。这里的步骤比较多,我们就从selectImports
倒序说起,这里分为几点来说:
- 首先,
AutoConfigurationImportSelector
继承了接口ImportSelector
org.springframework.context.annotation.ConfigurationClassParser
类通过接口org.springframework.context.annotation.ImportSelector
调用了selectImports
,这里调用的方法分别为#processDeferredImportSelectors
和#processImports
,最终的指向都是#parse(Set<BeanDefinitionHolder>)
方法。这里需要说明的是#processImports
方法就是对于处理@Import
注解的相关方法,该类的源码中注释有说明。- 而
org.springframework.context.annotation.ConfigurationClassParser#parse(Set<BeanDefinitionHolder>)
却是org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
调用,而#processConfigBeanDefinitions
为自身方法所调用。 ConfigurationClassPostProcessor
调用的源头是类org.springframework.context.support.PostProcessorRegistrationDelegate
,这个类中有两个公共的调用方法。- 最后由
org.springframework.context.support.AbstractApplicationContext#refresh
调用 org.springframework.context.support.AbstractApplicationContext#refresh
方法在org.springframework.boot.SpringApplication#run
被调用了
因此,这里就与我们上面介绍的SpringApplication.run
产生了联系,就是通过其调用了抽象上下文AbstractApplicationContext的refresh方法,从而产生了上面的一系列步骤。
可以看下UML类图了解它们关系
可能这里说的不清楚,建议使用IDE进行debug看源码。而且这里对于Spring Context的内容没有展开,本人也一知半解(或者说不解,不了解),望见谅,有需要可以参考以下文章
https://docs.spring.io/spring/docs/5.2.0.BUILD-SNAPSHOT/spring-framework-reference/core.html#spring-core
https://www.cnblogs.com/davidwang456/p/5717972.html
https://blog.csdn.net/yangyangiud/article/details/79835594
3. 总结
阅读了别人写的代码,看别人为何这么写,这里看到的就是对于接口的活用,对于封装以及工厂模式的应用,对于扩展,文件配置等等,自己能学到的还有很多,继续敲代码,看代码,向人家学习
参考链接:
https://docs.spring.io/spring-boot/docs/2.1.4.RELEASE/reference/htmlsingle/
https://www.cnblogs.com/saaav/tag/spring boot/
我的Spring Boot学习记录(一):自动配置的大致调用过程的更多相关文章
- 我的Spring Boot学习记录(二):Tomcat Server以及Spring MVC的上下文问题
Spring Boot版本: 2.0.0.RELEASE 这里需要引入依赖 spring-boot-starter-web 这里有可能有个人的误解,请抱着怀疑态度看. 建议: 感觉自己也会被绕晕,所以 ...
- Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客
==他的博客应该不错,没有细看 Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客 http://blog.csdn.net/u012706811/article/det ...
- 自定义的Spring Boot starter如何设置自动配置注解
本文首发于个人网站: 在Spring Boot实战之定制自己的starter一文最后提到,触发Spring Boot的配置过程有两种方法: spring.factories:由Spring Boot触 ...
- Spring Boot源码探索——自动配置的内部实现
前面写了两篇文章 <Spring Boot自动配置的魔法是怎么实现的>和 <Spring Boot起步依赖:定制starter>,分别分析了Spring Boot的自动配置和起 ...
- Spring boot运行原理-自定义自动配置类
在前面SpringBoot的文章中介绍了SpringBoot的基本配置,今天我们将给大家讲一讲SpringBoot的运行原理,然后根据原理我们自定义一个starter pom. 本章对于后续继续学习S ...
- 【转载】Spring boot学习记录(一)-入门篇
前言:本系列文章非本人原创,转自:http://tengj.top/2017/04/24/springboot0/ 正文 首先声明,Spring Boot不是一门新技术.从本质上来说,Spring B ...
- Spring boot 学习记录
java的三种配置方式 基于xml的配置 基于注解的配置 基于java的配置 Spring boot推荐的配置方式:java配置+注解配置 一.注解 SpringBootApplication :等价 ...
- 2019-04-05 Spring Boot学习记录
1. 使用步骤 ① 在pom.xml 增加父级依赖(spring-boot-starter-parent) ② 增加项目起步依赖,如spring-boot-starter-web ③ 配置JDK版本插 ...
- 【转载】Spring boot学习记录(三)-启动原理解析
前言:本系列文章非本人原创,转自:http://tengj.top/2017/04/24/springboot0/ 正文 我们开发任何一个Spring Boot项目,都会用到如下的启动类 @Sprin ...
随机推荐
- C#-Xamarin的Activity传值与Fragment引用
前言 我们学习任何一个新框架时,肯定都需要学习它的子页面用法,因为子页面是封装公共内容最好的容器. 在Xamarin里子页面为Fragment,翻译过来是片段的意思. Fragment 下面我们来学习 ...
- 使用PixiJS做一个小游戏
PixiJS PixiJS使用WebGL,是一个超快的HTML5 2D渲染引擎.作为一个Javascript的2D渲染器,Pixi.js的目标是提供一个快速的.轻量级而且是兼任所有设备的2D库. 官方 ...
- 内部类、异常以及 LeetCode 每日一题
1 内部类 内部类的作用: 内部类提供了更好的封装,可以把内部类隐藏于外部类之内,不允许同一个包中的其他类访问该类.(例如给“牛”这个类组合一个“牛腿”,则可以把牛腿定义成内部类,因为牛腿脱离了牛没有 ...
- Eclipse4JavaEE安装Gradle,并导入我们的Gradle项目
第一步:下载Gradle Gradle下载链接,如下图,下载最新版本即可.下载下来的zip包,解压到一个目录即可,如F盘 第二步:配置环境变量 首先添加GRADLE_HOME,如下图 然后在Path下 ...
- DFS(深度优先搜索)
简介 DFS的过程是一个递归过程,它是从图中的某个顶点开始,首先访问起始点v,然后选择一个与顶点v相邻的且没有被访问的顶点w,以w为起始顶点,在进行DFS,直到图中所有与v相邻的顶点都被访问过为止. ...
- 设计模式之过滤器模式——Java语言描述
过滤器模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来 实现 创建一个Person对象.Criteria 接口和实现了该接口的实体类,来过滤 Person 对象的列 ...
- Rest_framework Router 路由器(含SimplyRouter源码浅解)
目录 Rest_framework Router 路由器 ViewSet结合Router,自动生成url. 将ViewSet注册到Router中,需要三个要素: 关于路由规则,细分有四类: rest_ ...
- Hi3516EV300专业型HD IP Camera SoC
Hi3516EV300芯片特点: 处理器内核 ARM Cortex A7@ 900MHz,32KB I-Cache,32KB D-Cache /128KB L2 cache 支持 Neon 加速,集成 ...
- Android开发—— Tablayout的使用
Tablayout的使用 属性 属性名 说明 app:tabMod 设置Tab模式 app:tabTextColor 设置文本颜色 app:tabSelectedTextColor 设置选中文本颜色 ...
- [题解] 2038: [2009国家集训队]小Z的袜子(hose)
莫队,卡常数 题目地址 思路 设\(\text{Vis[i]}\)为元素\(\text{i}\)在区间\(\text{[L,R]}\)的出现次数 考虑区间\(\text{[L,R]}\)和元素\(\t ...