您的当前位置:首页正文

Springboot的启动过程总结

来源:一二三四网
Springboot的启动过程总结

1、总体步骤分两个

创建Spring 应⽤(创建SpringApplication)运⾏Spring 应⽤(运⾏SpringApplication)

2、创建Spring 应⽤(创建SpringApplication)

就是new ⼀个对象,构造器⾥⾯做⼀些初始化的动作

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.resourceLoader = resourceLoader;

Assert.notNull(primarySources, \"PrimarySources must not be null\");

this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //判断应⽤的类型deduce v. 推论; 推断; 演绎;的意思

this.webApplicationType = WebApplicationType.deduceFromClasspath(); //设置初始化器,保存起来 从spring.factories 找

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //设置监听器、保存起来从spring.factories 找

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //主应⽤类是那个

this.mainApplicationClass = deduceMainApplicationClass(); }

3、运⾏Spring 应⽤(运⾏SpringApplication)

// args 就是我们java-jar 时传的命令⾏参数

public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start();

ConfigurableApplicationContext context = null;

Collection exceptionReporters = new ArrayList<>(); //设置应⽤进⼊Headless 模式 configureHeadlessProperty();

//获取所有的运⾏监听器:就⼀个EventPublishingRunListener从spring.factories 中找的 SpringApplicationRunListeners listeners = getRunListeners(args);

//通知对系统启动过程中的⼈,项⽬正在启动,让所有的监听器感知到项⽬正在启动,这⾥就包括我们⾃⼰定义的监听器 listeners.starting(); try {

//保存我们传过来的命令⾏参数

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //准备环境,

ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment);

Banner printedBanner = printBanner(environment);

//创建IOC容器,这⾥⾄关重要。这个没有特别的,根绝应⽤的类型来创建⼀个SERVLET容器 context = createApplicationContext();

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //准备ioc 容器

prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新ioc 容器

refreshContext(context);

afterRefresh(context, applicationArguments); stopWatch.stop();

if (this.logStartupInfo) {

new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); }

//通知所有的监听器应⽤启动完成了 listeners.started(context);

//获取容器中的ApplicationRunner 和CommandLineRunner ,然后遍历所有的runner,执⾏runner的run⽅法,runner⼀般都是我们⾃⼰写的 callRunners(context, applicationArguments); }

catch (Throwable ex) {

handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); }

try {

//调⽤所有监听器的running ⽅法,对程序运⾏过程进⾏监听。 listeners.running(context); }

catch (Throwable ex) {

handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex);

}

return context;}

3.1 prepareEnvironment ⽅法说明

在这⼀步,SpringApplication会创建Spring启动所需的环境,这个环境主要由ConfigurableEnviroment对象表⽰。 ⾸先,该对象确认了程序是否需要设置Web环境,其次,该对象还确定了程序所需要的参数和读取的配置⽂件等信息。

/** *

* 获取或者创建⼀个Environment对象,如果我们⾃⼰在main ⽅法⾥⾯创建了⼀个Environment,并放到 * SpringApplication 中了就不创建了,

* SpringApplication application = new SpringApplication(ProfileDemoApplication.class); * ConfigurableEnvironment environment = new StandardEnvironment(); * environment.setActiveProfiles(\"dev\"); * application.setEnvironment(environment); * 否则就创建 * * */

注意:以下的说明

// 添加初始的properties(注意:当前并未加载如application.properties/yml的properties)// 添加初始的profile(注意:当前并未加载如application.properties/yml配置profile)

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { //创建或者获取⼀个Environment 对象

ConfigurableEnvironment environment = getOrCreateEnvironment(); //配置environment

configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment);

// 触发监听器(主要是触发ConfigFileApplicationListener,这个监听器将会加载如application.properties/yml这样的配置⽂件) //通知所有的监听器环境准备好了,包括我门⾃⼰定义的监听器。 listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) {

environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); }

return environment; }

3.1.1 configureEnvironment

//配置环境

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { if (this.addConversionService) {

ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService)conversionService); }

//把命令⾏参数、外部配置⽂件的属性(利⽤@PropertySource指定的⽂件),系统属性都配置到环境⾥⾯、加载配置⽂件 this.configurePropertySources(environment, args); //激活profile环境的

this.configureProfiles(environment, args); }

3.2 prepareContext

/** * *

* 虽然已经得到了ApplicationContext对象,但此时的对象还只是⼀个空⽩对象,需要准备和处理后,ApplicationContext才能被使⽤。 *

* 在准备过程中主要做了做了⼏件事:

* 为ApplicationContext设置之前准备好的Environment对象、也就是说把Environment 组件放到容器⾥⾯ *

* 通过对ApplicationContext后置处理或是BeanDefinitionLoader等⽅式往容器中添加⼀些初始的Bean。 *

* 应⽤默认的初始化器初始化应⽤程序上下⽂(责任链模式的应⽤,多个初始化器形成⼀个List,应⽤程序需要被每个初始化器应⽤⼀次,每个初始化器有⾃⼰的职责)。 *

* 准备过程中ApplicationRunListener发出两个消息,分别是contextPrepared和contextLoaded。

*/

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,

SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 给上下⽂对象设置环境对象,给AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner设置环境对象 context.setEnvironment(environment);

// 1. 上下⽂后置处理,包括向BeanFactory中注册BeanNameGenerator和ConversionService postProcessApplicationContext(context);

// 2. SpringApplication构造器中初始化了各种ApplicationContextInitializer(应⽤初试化器), //循环调⽤他们的initialize⽅法、对ioc 容器进⾏扩展 applyInitializers(context);

// 遍历所有的监听器,通知监听器上下⽂准备好了。 listeners.contextPrepared(context); if (this.logStartupInfo) {

// 打印启动信息,包括pid,⽤户等

logStartupInfo(context.getParent() == null); // 答应profile信息

logStartupProfileInfo(context); }

// Add boot specific singleton beans

ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 将命令⾏参数组件注册到容器⾥⾯

beanFactory.registerSingleton(\"springApplicationArguments\ if (printedBanner != null) { // 将Banner注册到容器中

beanFactory.registerSingleton(\"springBootBanner\ }

if (beanFactory instanceof DefaultListableBeanFactory) {

// 设置不允许定义同名的BeanDefinition,重复注册时抛出异常 ((DefaultListableBeanFactory) beanFactory)

.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); }

if (this.lazyInitialization) {

// 如果懒加载,添加懒加载后置处理器

context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); }

// Load the sources

Set sources = getAllSources();

Assert.notEmpty(sources, \"Sources must not be empty\"); load(context, sources.toArray(new Object[0])); //遍历所有的监听器,IOC容器加载好了 listeners.contextLoaded(context);}

3.3 refreshContext()——刷新上下⽂

在应⽤程序上下⽂准备好后,可以通过刷新应⽤程序上下⽂发现Bean并加载到容器中,主要是⽬的是加载beanrefreshContext()会调⽤ApplicationContext.refresh()⽅法。

AbstractApplicationContext中定义了refresh()⽅法的基本框架(模板模式的应⽤)。

3.3.1 refresh()

这个是spring经典的容器初始化过程,创建所有的组件

因篇幅问题不能全部显示,请点此查看更多更全内容

Top