下一篇:https://blog.csdn.net/NEU_LightBulb/article/details/102892314
一、RUN
1.Spring Boot 启动
SpringBoot项目入口在@SpringBootApplication注解的类的main方法中。在main方法中有两种启动Spring项目方式,一种是简而明了的:
SpringApplication.run(YourApplication.class, args);
和可以自定Banner、Web属性、记录器等配置项的:
new SpringApplicationBuilder(YourApplication.class)
.web(... ...) //配置Web类型
//... ... many many config
.run(args);
不管是哪一种,最后都会调用类SpringApplication中的run方法,方法内容及逻辑如下:
// 创建 StopWatch 对象,用于统计 run 方法启动时长。
StopWatch stopWatch = new StopWatch();
// 启动统计。
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置 headless 属性。
configureHeadlessProperty();
// 获得 SpringApplicationRunListener 数组
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听,遍历 SpringApplicationRunListener 数组每个元素,并执行。
listeners.starting();
try {
//创建 ApplicationArguments 对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 加载属性配置,包括所有的配置属性application.properties 等
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印 Banner
Banner printedBanner = printBanner(environment);
// 创建容器
context = createApplicationContext();
// 异常报告器
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);
// 调用 ApplicationRunner 和 CommandLineRunner 的运行方法。
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;
这里我们按顺序关注几个方法,第一个createApplicationContext,在这个方法中会根据你的Web类型设置返回对应的Context类型。可以在 SpringApplicationBuilder中用.web()设置,也可以在配置文件中加入spring.main.web-application-type。不同类型对应结果如下:
SERVLET:org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
应用程序应作为基于servlet的Web应用程序运行,并应启动嵌入式servlet Web服务器。
REACTIVE:org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
应用程序应作为响应式Web应用程序运行,并应启动嵌入式响应式Web服务器。
default/NONE:org.springframework.context.annotation.AnnotationConfigApplicationContext
应用程序不应作为Web应用程序运行,也不应启动嵌入式Web服务器。
第二个是prepareContext,这个方法首先将Environment添加到Context,然后向Context中的beanFactory注入一个"internalConfigurationBeanNameGenerator"(内部可配置的bean名称生成器),同时注入ResourceLoader和ClassLoader。
第三个是refreshContext,这个方法比较重要,按照他的逻辑顺序详细说一下。
this.prepareRefresh();
这个方法会清除classpath下的一些bean缓存,设置context的启动和关闭状态,初始化包括Servlet的一些init参数。
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
这个两步是获取最新的beanFactory,添加bean的类加载器和bean表达式解析器,注册相关的可解析依赖,添加一些BeanPostProcessor以及注册一些单例bean。
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
这两步是获取应用定义的bean的名称的集合,将这些bean的名称放到beanFactory中的变量beanDefinitionNames中,向beanFactory的manualSingletonNames变量,添加一些不在Spring的容器内(不受Spring控制的)bean。
this.onRefresh();
重点来了,刚刚提到有根据设置我们的Context会有不同的类型。不同的区别之一就是,每一个类型有自己独特的主题Theme和onRefresh方法。以SERVLET为例,这种Context继承ServletWebServerApplicationContext,使用其onRefresh方法。该方法首先执行GenericWebApplicationContext类中的onRefresh(都叫一个名别乱),获取并装配主题Theme。之后再执行自己类中的createWebServer方法,这个方法先getWebServerFactory再获取WebServer(当前Springboot使用的Web容器)。附一张图吧:
注意,依赖中的spring-boot-starter-web已经默认集成了spring-boot-starter-tomcat作为Web容器,所以这里Tomcat Web Server被装配进来了。
2.CommandLineRunner和ApplicationRunner
SpringBoot中提供了两个接口可以在Spring Boot启动的过程中进行一些额外的操作,比如读取配置文件、数据库操作等自定义的内容。而这些功能的实现也非常简单,直接实现这两个接口并实现其run方法,然后将该类实例化,并通过@Component将其注册为Spring的一个bean即可。同时也可以通过@Order或实现Ordered接口来对其指定执行顺序。
这个实现的run方法会在上面给出的SpringApplication中的run方法的try的最后的callRunners方法中执行。
下一篇:https://blog.csdn.net/NEU_LightBulb/article/details/102892314