Spring Boot 零配置启动原理
在创建传统 SpringMVC 项目时,需要复杂的配置文件,例如:
web.xml
,加载配置 spring 容器,配置拦截application.xml
,配置扫描包,扫描业务类springmvc.xml
,扫描 controller,视图解析器等……
而 Spring Boot 为我们提供了一种极简的项目搭建方式,看一下 Spring Boot 项目的启动类:
简单的一行代码,即可启动一个 Spring Boot 程序,那么在实际运行中是如何做到零配置启动的呢?下面从源码角度进行分析。
@SpringBootApplication
首先看一下@SpringBootApplication
这个组合注解,除去元注解外,它还引入了其他三个重要的注解:
@SpringBootConfiguration
从源码可以看到,其实@SpringBootConfiguration
并没有额外功能,它只是 Spring 中@Configuration
的派生注解,用于标注配置类,完成 Bean 的配置与管理。
@ComponentScan
Spring 中的注解,用于包的扫描,并把声明了特定注解的类交给 spring 的 ioc 容器。
@EnableAutoConfiguration
Spring Boot 有中一个非常重要的理念就是约定大于配置。而自动配置这一机制的核心实现就是靠@EnableAutoConfiguration
注解完成的。
可以看出,在@EnableAutoConfiguration
注解中,使用@Import
导入了AutoConfigurationImportSelector
这个类,实现了ImportSelector
接口的selectImports()
方法。spring 中会把selectImports()
方法返回的 String 数组中的类的全限定名实例化为 bean,并交给 spring 容器管理。
查看其中的getAutoConfigurationEntry
方法:
在执行完getCandidateConfigurations
后,把众多类的全限定名存储到了一个 List 中。
SpringFactoriesLoader
这个类非常重要,属于 Spring 框架的一种扩展方案,提供一种了配置查找的功能支持。其主要功能就是读取配置文件META-INF/spring.factories
,决定要加载哪些类。
当然,并不是所有spring.factories
中的类都会被加载到 spring 容器中,很多情况下需要按照需求所需的情况引入,依赖条件注解@Conditional
进行判断。例如ServletWebServerFactoryAutoConfiguration
类
只有在classpath
下存在ServletRequest
这一类时,才将ServletWebServerFactoryAutoConfiguration
作为配置类导入 spring 容器中。
SpringApplication
SpringApplication
提供了一个简单的方式以启动 Spring boot 程序,查看SpringApplication.run
方法调用。在此创建了一个SpringApplication
的实例,并调用了它的 run 方法:
看一下创建实例的过程源码:
主要完成了这几件事情:
设置资源加载器,用于将资源加载到加载器中
判断当前项目类型是什么? 提供了
NONE
,SERVLET
,REACTIVE
三种类型备选使用
SpringFactoriesLoader
查找并加载所有可用的ApplicationContextInitializer
使用
SpringFactoriesLoader
查找并加载所有可用的监听器ApplicationListener
推断并设置
main
方法的定义
SpringApplication
完成初始化后,调用run
方法,下面对run
方法中核心代码进行分析:
按照图中标注序号进行分析:
1、spring 监听器的使用,要获取这些监听器的对象,就要知道其全路径。通过SpringFactoriesLoader
查找spring.factories
获得,之后再调用它们的started()
方法
2、 创建并配置当前 Spring Boot 应用将要使用的 Environment,根据监听器和默认应用参数来准备所需要的环境
3、打印 Banner
4、创建 spring 应用上下文。根据之前推断的项目类型,决定该为当前 SpringBoot 应用创建什么类型的ApplicationContext
并创建完成
5、准备应用上下文,首先将之前准备好的 Environment 设置给创建好的ApplicationContext
使用。然后遍历调用所有ApplicationContextInitializer
的initialize
方法来对已经创建好的ApplicationContext
进行进一步的处理。最后,遍历调用所有SpringApplicationRunListener
的contextPrepared()
方法
6、这里最终调用了Spring中AbstractApplicationContext
的refresh
方法,可以说这个refresh
方法是 Spring 中最重要的方法之一,完成了 Bean 工厂创建,后置管理器注册,Bean 实例化等最重要的工作。这一步工作完成后,spring 的 ioc 容器就完成了
7、如果有 Bean 实现了CommandLineRunner
接口并重写了run
方法,则遍历执行CommandLineRunner
中的方法
手写 Starter
Starter 是 Spring boot 的核心思想之一,在使用 spring boot 来搭建项目时,往往只需要引入官方提供的 starter,就可以直接使用,而不用再进行复杂的配置工作。
一方面,是前面说过的通过动态 spi 扩展可以直接从 starter 的META-INF/spring.factories
中决定什么类将被实例化为 bean 交给 spring 容器管理。另一方面,starter 的父 pom 中往往已经包含了需要导入的依赖,以mybatis-spring-boot-starter
这一 starter 为例,点开后可以看见它已经将依赖的坐标全部为我们导入了。
总的来说,使用 starter 可以完成以下功能:
启用功能,注意不是实现功能
依赖管理,starter 帮我们引入需要的所有依赖
讲完了关于 starter 的原理,下面讲讲如何构造一个自己的 starter。官方为我们提供了一个命名规范,建议第三方 starter 命名应当遵循thirdpart-spring-boot-starter
这一格式,那我们就来手写一个my-spring-boot-starter
,通过这个过程来学习如何完成属性的配置。
1、创建一个 maven 的普通 project,在 pom 中添加 parent 节点
2、引入自动装配的依赖
3、实现自己的功能需求
如果希望能够在其他项目中使用的时候,通过 yml 或 property 文件对这个属性进行赋值,就要写一个对属性进行赋值操作的类,并使用@ConfigurationProperties
注解
4、如果希望上面开发的功能在 springboot 启动的时候就加入项目进行管理,就需要有一个代表当前 starer 自动装配的类
5、在resources
创建META-INF
,创建spring.factories
文件,在里面写入:
6、使用 maven 打包
测试工程
1、新建一个测试工程,在 pom 文件中引入上面打包的坐标
2、使用 yml 进行属性的配置
3、运行测试
结果:
如果在之前为 name 设置了默认值,那么在不在 yml 中对 name 进行配置的话就会打印默认值。这也就是为什么 springboot 在启动 tomcat 时会自动为我们设置为 8080 端口的原因,从这再一次体现了“约定大于配置”这一理念。
最后
如果觉得对您有所帮助,小伙伴们可以点赞、转发一下,非常感谢
公众号
码农参上
,加个好友,做个点赞之交啊
版权声明: 本文为 InfoQ 作者【码农参上】的原创文章。
原文链接:【http://xie.infoq.cn/article/46550a37cb5ca5189d215be88】。文章转载请联系作者。
评论