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的配置类。引入一个依赖,例如:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>

这个starter只是引入了其必须的依赖,没有做任何工作,最主要的是有个依赖为spring-boot-starter,这里包含了一个spring.factories,里面就有各种自动配置的EnableAutoConfiguration

2.2. 怎么加载EnableAutoConfiguration

2.2.1 SpringApplication.run

此函数是一个Spring Boot项目的入口,这里与@SpringBootApplication有很大的关联。

Spring Boot项目一般的启动代码如下:

  1. @SpringBootApplication
  2. public class SpringBootDemoApplication {
  3. public static void main(String[] args) {
  4. //主要提供了一个静态函数run来调用
  5. SpringApplication.run(SpringBootDemoApplication.class, args);
  6. }
  7. }

再看run函数

  1. public static ConfigurableApplicationContext run(Class<?> primarySource,
  2. String... args) {
  3. return run(new Class<?>[] { primarySource }, args); //
  4. }
  5. public static ConfigurableApplicationContext run(Class<?>[] primarySources,
  6. String[] args) {
  7. return new SpringApplication(primarySources).run(args);
  8. }
  9. // 最终被调用的run函数
  10. public ConfigurableApplicationContext run(String... args) {
  11. //......
  12. context = createApplicationContext();
  13. prepareContext(context, environment, listeners, applicationArguments,
  14. printedBanner);
  15. refreshContext(context);
  16. //......
  17. return context;
  18. }

以上中,最终是调用了org.springframework.boot.SpringApplication#run(java.lang.String...)方法,此方法主要是准备了Environment和ApplicationContext。而ApplicationContext就是Spring项目核心的东西,那与自动配置又有什么关系,这里就需要回去看下@SpringBootApplication注解

2.2.2 @SpringBootApplication

  1. //....
  2. @EnableAutoConfiguration
  3. public @interface SpringBootApplication {
  4. //.....
  5. }
  6. //在上面@SpringBootApplication的注解代码中,有个@EnableAutoConfiguration
  7. //.....
  8. @Import(AutoConfigurationImportSelector.class)
  9. public @interface EnableAutoConfiguration {
  10. //...
  11. }

上面为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的源码:

  1. /**
  2. * {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration
  3. * auto-configuration}. This class can also be subclassed if a custom variant of
  4. * {@link EnableAutoConfiguration @EnableAutoConfiguration}. is needed.
  5. *
  6. * 主要的意思为:DeferredImportSelector能够去处理 EnableAutoConfiguration自动配置类Import工作
  7. */
  8. public class AutoConfigurationImportSelector
  9. implements DeferredImportSelector,... {
  10. /**
  11. * 返回一个需要被加载至Spring上下文的的类名数组
  12. */
  13. @Override
  14. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  15. //....
  16. List<String> configurations = getCandidateConfigurations(annotationMetadata,
  17. attributes);
  18. //....
  19. return StringUtils.toStringArray(configurations);
  20. }
  21. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
  22. AnnotationAttributes attributes) {
  23. //通过SpringFactoriesLoader加载出所有的EnableAutoConfiguration类
  24. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
  25. getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
  26. return configurations;
  27. }
  28. /**
  29. * 返回了一个让SpringFactoriesLoader加载的Class,就是EnableAutoConfiguration
  30. */
  31. protected Class<?> getSpringFactoriesLoaderFactoryClass() {
  32. return EnableAutoConfiguration.class;
  33. }
  34. }

那么究竟是谁去调用了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学习记录(一):自动配置的大致调用过程的更多相关文章

  1. 我的Spring Boot学习记录(二):Tomcat Server以及Spring MVC的上下文问题

    Spring Boot版本: 2.0.0.RELEASE 这里需要引入依赖 spring-boot-starter-web 这里有可能有个人的误解,请抱着怀疑态度看. 建议: 感觉自己也会被绕晕,所以 ...

  2. Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客

    ==他的博客应该不错,没有细看 Spring Boot学习记录(二)--thymeleaf模板 - CSDN博客 http://blog.csdn.net/u012706811/article/det ...

  3. 自定义的Spring Boot starter如何设置自动配置注解

    本文首发于个人网站: 在Spring Boot实战之定制自己的starter一文最后提到,触发Spring Boot的配置过程有两种方法: spring.factories:由Spring Boot触 ...

  4. Spring Boot源码探索——自动配置的内部实现

    前面写了两篇文章 <Spring Boot自动配置的魔法是怎么实现的>和 <Spring Boot起步依赖:定制starter>,分别分析了Spring Boot的自动配置和起 ...

  5. Spring boot运行原理-自定义自动配置类

    在前面SpringBoot的文章中介绍了SpringBoot的基本配置,今天我们将给大家讲一讲SpringBoot的运行原理,然后根据原理我们自定义一个starter pom. 本章对于后续继续学习S ...

  6. 【转载】Spring boot学习记录(一)-入门篇

    前言:本系列文章非本人原创,转自:http://tengj.top/2017/04/24/springboot0/ 正文 首先声明,Spring Boot不是一门新技术.从本质上来说,Spring B ...

  7. Spring boot 学习记录

    java的三种配置方式 基于xml的配置 基于注解的配置 基于java的配置 Spring boot推荐的配置方式:java配置+注解配置 一.注解 SpringBootApplication :等价 ...

  8. 2019-04-05 Spring Boot学习记录

    1. 使用步骤 ① 在pom.xml 增加父级依赖(spring-boot-starter-parent) ② 增加项目起步依赖,如spring-boot-starter-web ③ 配置JDK版本插 ...

  9. 【转载】Spring boot学习记录(三)-启动原理解析

    前言:本系列文章非本人原创,转自:http://tengj.top/2017/04/24/springboot0/ 正文 我们开发任何一个Spring Boot项目,都会用到如下的启动类 @Sprin ...

随机推荐

  1. 让你分分钟理解 JavaScript 闭包

    闭包,是 Javascript 比较重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是 ECMAScript 规范给的定义,如果没有实战经验,很难从定义去理解它.因此,本文不会对闭包的概 ...

  2. 3D数学 矩阵常用知识点整理

    1.矩阵了解 1)矩阵的维度和记法 (先数多少行,再数多少列) 2)矩阵的转置 行变成列,第一行变成第一列...矩阵的转置的转置就是原矩阵            即        3)矩阵和标量的乘法 ...

  3. [翻译 EF Core in Action 1.11] 何时不应该使用EF Core

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  4. 【php性能优化】关于写入文件操作的取舍方案

    对于使用php对文件进行写入操作有两种方案一种使用 file_put_contents() 和 fopen()/fwrite()/fclose() 两种方案至于应该怎么选,我觉得应该分情况选择,下面是 ...

  5. css对齐方案总结

    css对齐方案总结 垂直居中 通用布局方式(内敛元素和块状元素都适用) 利用flex:核心代码: 12345 .container{ display:flex; flex-direction:colu ...

  6. [ArcGIS API for JavaScript 4.8] Sample Code-Get Started-layers简介

    [官方文档:https://developers.arcgis.com/javascript/latest/sample-code/intro-layers/index.html] 一.Intro t ...

  7. Android之日志管理(Log)

    ##文章大纲一.为什么要使用日志管理工具二.日志管理工具实战三.项目源码下载 ##一.为什么要使用日志管理工具###1. 对IT安全至关重要  当您使用强大的日志管理软件自动触发以保护您的系统时,您已 ...

  8. svn版本控制迁移到git

    获得原 SVN 仓库使用的作者名字列表 因为导入到git需要配置原作者(svn提交人)和git账户的映射关系 其格式为: vim authors-transform.txt taoxs = xsTao ...

  9. 物理dataguard 正常切换 脚色转换,switchover_status 状态改变

    正常切换切换前: 主库:SQL> select DATABASE_ROLE from v$database;DATABASE_ROLE----------------PRIMARY SQL> ...

  10. Windows迁移打印机与打印队列

    移动打印机时,打印机当前所在服务器为源服务器,打印机将迁移到的服务器为目的服务器. 步骤: 1.为源服务器创建打印机配置文件 printbrm -b -s Servername -f SaveFile ...