前言
关于SpringBoot自动装配的概念:
- 1)SpringBoot启动会加载大量的自动配置类
- 2)xxxAutoCOnfiguration:自动配置类
- 3)自动配置类中会配置相应的组件,这些组件不需要我们手动配置
- 4)在给容器自动配置类添加组件的时候,会从xxxProperties类中获取某些属性,我们可以在yml、properties配置文件中修改这些属性值
一. 启动原理
1.主配置类入口
@SpringBootApplication
public class DevServiceApplication {
public static void main(String[] args) {
SpringApplication.run(DevServiceApplication.class,args);
}
}
SpringApplication这个类主要做了4件事情:
1.
推
断
应
用
的
类
型
是
普
通
项
目
还
是
W
e
b
项
目
\color{blue}{1. 推断应用的类型是普通项目还是Web项目}
1.推断应用的类型是普通项目还是Web项目
2.
查
找
并
加
载
所
有
的
可
用
初
始
化
器
,
设
置
到
i
n
i
t
i
a
l
i
z
e
r
s
属
性
中
\color{blue}{2. 查找并加载所有的可用初始化器,设置到initializers属性中}
2.查找并加载所有的可用初始化器,设置到initializers属性中
3.
找
出
所
有
的
应
用
程
序
监
听
器
,
用
于
处
理
上
下
文
获
取
B
e
a
n
,
设
置
到
l
i
s
t
e
n
e
r
s
属
性
中
\color{blue}{3. 找出所有的应用程序监听器,用于处理上下文获取Bean,设置到listeners属性中}
3.找出所有的应用程序监听器,用于处理上下文获取Bean,设置到listeners属性中
4.
推
断
并
设
置
m
a
i
n
方
法
的
定
义
类
,
并
找
到
运
行
的
方
法
\color{blue}{4.推断并设置main方法的定义类,并找到运行的方法}
4.推断并设置main方法的定义类,并找到运行的方法
2.进入run方法
(new SpringApplication(primarySources)).run(args);
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
3.创建SpringApplication对象:
包括了初始化和赋值操作,配置Initializers和Listeners
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
二. 运行流程
调用SpringApplication对象的run()方法:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
该方法中实现了如下几个关键步骤:
1.创建了应用的监听器SpringApplicationRunListeners并开始监听
2.加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,其最终也是继承了ConfigurableEnvironment
3.配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)
4.创建run()返回对象:ConfigurableApplicationContext(应用配置上下文),根据环境决定创建Web的ioc还是普通的ioc
5.prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联
6.接下来的refreshContext(context)方法(初始化方法如下)将是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作
配置结束后,Springboot做了一些基本的收尾工作,返回了应用环境上下文。回顾整体流程,Springboot的启动,主要创建了配置环境(environment)、事件监听(listeners)、应用上下文(applicationContext),并基于以上条件,在容器中开始实例化我们需要的Bean,至此,通过SpringBoot启动的程序已经构造完成,接下来我们来探讨自动化配置是如何实现。
三. 自动配置原理
SpringBoot自动配置模块,主要使用到了SpringFactoriesLoader,即Spring工厂加载器
下图有助于我们形象理解自动配置流程:
mybatis-spring-boot-starter、spring-boot-starter-web等组件的META-INF文件下均含有spring.factories文件,自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean。
此部分主要参考博文:
https://blog.csdn.net/hfmbook/article/details/100507083
这是在我查看SpringBoot注解源码后绘制的流程图:
得出的结论是:
SpringBoot所有自动配置都会在启动类中自动被扫描和加载,存放在SpringBoot_autoconfigure_META-INF下的spring.factories里,但需要我们手动导入对应的starter才会启动。
1.SpringBoot在启动时,会从类路径/META-INF里的spring.factories里获取指定的配置类:
2.将这些自动配置的类导入容器,自动配置类就会生效,由Spring容器帮我们自动配置
3.以前我们要自己配置的东西,现在都由SpringBoot帮我们自动完成了
4.整个JavaEE的解决方案和自动配置的资源都在spring-boot-autoconfigure-2.2.5.RELEASE.jar这个核心包下
5.核心包里的spring.factories包含了所有的配置类,以XXXAutoConfiguration的全类名文件存在,这些文件会向容器导入所有这个场景所需的组件,并自动配置@Configuration