SpringBoot如果要说是SpringFrameWork的封装那么也不全是,个人认为从思想上来说是两个东西。
SpringBoot主要具备如下特性
1.自动装配
2.内置tomcat等web容器
3.与devops天然集成,这也可以认为是微服务的特点。
首先创建SpringApplication对象期间初始化程序运行环境拉取listener以及获取ApplicationContextInitializer
接着会运行run方法具体会执行如下步骤
1.获取并启动监听器
2.构造容器环境,加载application配置文件或者自定义配置文件位置
3.创建容器
4.准备预处理容器
5.刷新容器
6.刷新容器后调用扩展接口
经过上面几步整个程序就运行起来了
下面咱们看下代码
下面是一段程序的启动入口代码,这块代码其实可以自己自定义new一个SpringApplication对象启动
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
咱们跟进run方法中去,会看到下面这一段代码,。这里如上面开头所说有两个步骤
1.初始化SpringApplication对象
2.执行run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
先看初始化SpringApplication,咱们点进去看一下构造函数干了些啥
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//resourceLoader加载配置资源但是没在这初始化,后面再说
this.resourceLoader = resourceLoader;
//默认是启动类,不过有可能你自定义了一个配置类放到main class运行就需要配置一下
Assert.notNull(primarySources, "PrimarySources must not be null");
//springApplication配置类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//设置servlet环境
this.webApplicationType = deduceWebApplicationType();
获取ApplicationContextInitializer,也是在这里开始首次加载spring.factories文件
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//获取监听器,这里是第二次加载spring.factories文件
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
上面的重点其实都在setInitializers与setListeners
这两个的初始化其实是SPI的Spring实现
getSpringFactoriesInstances这个方法挺有意思的
原理就是获取配置文件下type是哪一类的key配置的class名,然后通过反射实例化。
咱们继续往下看run方法
上一段代码,在代码里面有注释。先看看
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//第一步:获取并启动监听器.org.springframework.boot.context.event.EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//第二步:构造容器环境,加载application配置文件或者自定义配置文件位置
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//设置需要忽略的bean
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
//第三步:创建容器
context = createApplicationContext();
//第四步:实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//第五步:准备容器
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//第六步:刷新容器
refreshContext(context);
//第七步:刷新容器后的扩展接口
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
首先监听器是一个好东西,每当程序执行到哪个步骤的时候就根据相应的Event去执行相应的方法
不过需要将实现了的接口类都加载进来才会去执行,这里的这个监听器是SpringApplicationRunListener
也可以认为是第一个执行的监听器,钩子类监听器拉起来一堆的其它监听
接着就构造Environment
然后就是容器的创建,预处理准备容器,刷新容器
最后执行刷新后的容器扩展接口整个程序如果没有报错则会启动。
总结一下
这篇是先大概将SpringBoot的大流程说一下,从俯视角度看全局做到心中有数。
我们后续再详细拆分一小块一小块的细写。
不然一上来就每行代码的过,这样看Spring源码容易陷进去出不来。
导致最后感觉啥都不会
欢迎扫码加入知识星球继续讨论