概要
Spring boot启动时的七大步骤如下:
- 获取并启动监听器
- 准备应用程序环境
- 控制台打印Banner
- 创建应用上下文对象
- 准备应用上下文
- 刷新应用程序上下文
- 后续处理
测试入口:
//标识启动类,它是一个复合注解,标识使用自动装配、通过扫描注解注入bean
@SpringBootApplication // ===> 自动装载进【入口 - 2 !】
public class Hibernate52Application {
public static void main(String[] args) {
SpringApplication.run(Hibernate52Application.class, args); // ===> boot启动进 【入口 - 1 !】
}
}
主流程代码:
// run方法(七大步骤)
//1、获取并启动监听器从spring.factories文件中加载
//2、准备应用程序环境
//3、打印banner
//4、创建应用上下文 AnnotationConfigServletWebServerApplicationContext
//5、准备 applicationContext
//6、刷新context-- call refresh mothod(核心)
//7、上下文刷新后触发(空方法)
public ConfigurableApplicationContext run(String... args) {
//计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start(); //开始计时
// 创建启动上下文对象,存放配置参数环境信息等
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 定义容器
ConfigurableApplicationContext context = null;
// awt
configureHeadlessProperty();
// 第一步:获取并启动监听器 已经在上面new的步骤放到缓存中了,现在取出来
SpringApplicationRunListeners listeners = getRunListeners(args); // ===> 自定义listener
//向监听器发布start事件,【发布订阅模式,也叫生产者消费者模式】
// 默认会有一个EventPublishingRunListener
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 对命令行参数包装成一个对象(ApplicationArguments),比如:--spring.profiles.active=dev
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // ===>
//第二步:准备应用程序环境(配置文件application.yml就是这里加载的)
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // ===>
// 配置要忽略哪些bean
configureIgnoreBeanInfo(environment);
//第三步: 打印banner
Banner printedBanner = printBanner(environment); // ===>
// 第四步:【重点】创建应用上下文 servlet对应的是 AnnotationConfigServletWebServerApplicationContext
context = createApplicationContext(); // ===>
context.setApplicationStartup(this.applicationStartup);
//第五步:准备 applicationContext
// refresh将会使用Annotation方式扫描启动main上的@ComponentScan。
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//第六步:context,ioc的领地。bean在这里被创建起来
// tomcat容器被启动起来~~~~
// 它藏在 ioc 的 onRefresh 这一步里
refreshContext(context);// ===> 进入ioc的领地,刷新 spring ioc 容器, 加载和扫描所有的bean
//第七步:创建完成后的扫尾工作
afterRefresh(context, applicationArguments);
stopWatch.stop();//停止计时
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 发布started事件
listeners.started(context);
//执行runner的run方法,它允许你在容器启动后做一些事情
callRunners(context, applicationArguments); //===>
} catch (Throwable ex) {
// 异常处理
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 触发running事件
listeners.running(context);
} catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
// 返回最终构建的容器对象
return context;
}
获取并启动监听器
这里的监听器,就是监听SpringBoot的整个启动流程,实现了SpringApplicationRunListener
接口,就可以完成监听,在启动的不同阶段,可以执行到不同的方法
//这里的启动监听就是我们需要监听SpringBoot的启动流程监听,实现SpringApplicationRunListener类即可监听
// 获取spring.factories中 key为SpringApplicationRunListener的对象实例。
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
// 通过从 spring.factories 中获取 SpringApplicationRunListener 类型的配置类
// 在new阶段已经放到缓存中了。
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup); // ===> getSpringFactoriesInstances
}
查看具体SpringApplicationRunListener
都有哪些方法
public interface SpringApplicationRunListener {
//当调用run方法后会立即调用,可以用于非常早期的初始化
default void starting(ConfigurableBootstrapContext bootstrapContext) {
starting();
}
@Deprecated
default void starting() {
}
//环境准备好之后调用
default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
environmentPrepared(environment);
}
@Deprecated
default void environmentPrepared(ConfigurableEnvironment environment) {
}
//在加载资源之前,ApplicationContex准备好之后调用
default void contextPrepared(ConfigurableApplicationContext context) {
}
//在加载应用程序上下文但在其刷新之前调用
default void contextLoaded(ConfigurableApplicationContext context) {
}
/**
* 上下文已经刷新且应用程序已启动且所有{@link CommandLineRunner commandLineRunner}
* 和{@link ApplicationRunner ApplicationRunners}未调用之前调用
*/
default void started(ConfigurableApplicationContext context) {
}
/**
* 当应用程序上下文被刷新并且所有{@link CommandLineRunner commandLineRunner}
* 和{@link ApplicationRunner ApplicationRunners}都已被调用时,在run方法结束之前立即调用。
*/
default void running(ConfigurableApplicationContext context) {
}
//在启动过程发生失败时调用
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
准备应用程序环境
创建并配置SpringBoot应用将要使用的Environment
//创建并配置SpringBoot应用将要使用的Environment
// 过程如下:
// 1、创建配置环境 ConfigurableEnvironment
// 2、加载属性文件资源
// 3、配置监听
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 根据不同的web类型创建不同实现的Environment对象,servlet环境下返回 StandardServletEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment(); // ===>
// 命令行参数 启动的时候 --spring.profiles.active=dev 等等那些
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 发送环境已准备完成事件,listerners= 前面准备过的监听器
listeners.environmentPrepared(bootstrapContext, environment); // ===> 发布事件
DefaultPropertiesPropertySource.moveToEnd(environment); // 如果有defaultProperties配置移到最后
// 其他参数
configureAdditionalProfiles(environment);
// 绑定环境到SpringApplication
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment; //返回去
}
控制台打印banner
private Banner printBanner(ConfigurableEnvironment environment) {
// banner模式,可以是console、log、off
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
//日志打印banner
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
//控制台打印banner
return bannerPrinter.print(environment, this.mainApplicationClass, System.out); // ===> go
}
Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
//获取banner.txt
Banner banner = getBanner(environment); // ===>
//开始打印
banner.printBanner(environment, sourceClass, out); // ===>
return new PrintedBanner(banner, sourceClass);
}
private Banner getBanner(Environment environment) {
Banners banners = new Banners();
banners.addIfNotNull(getImageBanner(environment)); // 图片banner,没有,jwt显示
banners.addIfNotNull(getTextBanner(environment)); // ===> 文字banner,走ResourceBanner
if (banners.hasAtLeastOneBanner()) {
return banners; //只要有自定义的,返回
}
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
return DEFAULT_BANNER; // ===> 默认使用SpringBootBanner()实现
}
创建应用上下文对象
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType); // ===> create
}
其中applicationContextFactory
变量如下:
private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
org.springframework.boot.ApplicationContextFactory#DEFAULT
//无论哪一种,都是Annotation的,也就是boot都是注解+扫包
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext(); // 这里!注意看类型~
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
准备应用上下文
org.springframework.boot.SpringApplication#prepareContext
/**
* Spring容器准备
*/
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//一堆的设置
context.setEnvironment(environment);//设置环境
postProcessApplicationContext(context);//设置上下文
// 执行所有ApplicationContextInitializer对象的initialize方法(这些对象是通过读取spring.factories加载)
applyInitializers(context);//设置初始化工作
// 发布上下文准备完成事件到所有监听器
listeners.contextPrepared(context);//触发监听器
// 这句容易误解,其实是关闭bootstrap,启用ApplicationContext
// 前期bootstrapContext用来处理yml等配置文件和环境,现在它的使命已经完成
// 下面交给ApplicationContext去加载bean,进入了spring ioc的领地。
// https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/DefaultBootstrapContext.html
// "when the BootstrapContext is closed and the ApplicationContext has been prepared"
bootstrapContext.close(context);
if (this.logStartupInfo) { //日志操作
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context); // The following profiles are active: dev
}
// 获取工厂 DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//参数对象
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
//banner对象
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
//是否覆盖bean
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) { //是否懒加载
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources(); // 所有的启动类,就是main函数启动时传进来的class
Assert.notEmpty(sources, "Sources must not be empty");
//注解加载 这里是对Hibernate52Application 进行加载
load(context, sources.toArray(new Object[0]));
// 发送上下文加载完成事件
listeners.contextLoaded(context);
}
org.springframework.boot.BeanDefinitionLoader#load(java.lang.Class<?>)
private void load(Class<?> source) {
if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
}
if (isEligible(source)) {
//将启动类注册到BD里去,source就是我们的启动类,一个@Configration
this.annotatedReader.register(source); // BeanDefinition创建完成,往回走
}
}
刷新应用程序上下文
ioc容器初始化,同时启动tomcat
//核心方法
private void refreshContext(ConfigurableApplicationContext context) {
// 往下走!
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
// ioc,refresh
refresh((ApplicationContext) context); // ===>
}
最终会调用到org.springframework.context.support.AbstractApplicationContext#refresh
方法,该方法会调用到,org.springframework.context.support.AbstractApplicationContext#onRefresh
,这个方法是空方法,子类会对他进行实现,而tomcat的启动就是在子类的实现中触发的。
后续处理
/**
* Called after the context has been refreshed.
*
* @param context the application context
* @param args the application arguments
*/
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}