Spring boot启动流程分析精简版

版本:spring boot 2.2.2
前言
spring boot
设计目的是为了简化
spring
应用初始搭建及开发过程。

优点: 可快速搭建微服务脚手架,无需再配置xml文件,内嵌servlet容器。

spring boot 在代码里直接main函数就可以启动,那到底是怎么去启动一个应用的呢?下面开始分析。

源码分析
public static void main(String[] args) {
//启动方式
SpringApplication.run(Application.class, args);
}
SpringApplication类:
public static ConfigurableApplicationContext run(Class primarySource, String… args) {
return run(new Class[] { primarySource }, args);
}

//返回应用上下文对象
public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}

//创建一个新实例,将加载应用程序上下文
public SpringApplication(ResourceLoader resourceLoader, Class… primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, “PrimarySources must not be null”);
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//获取web应用类型,返回servlet表示要启动对应的servlet容器,如tomcat
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//加载ApplicationContextInitializer实现类,首次加载spring.factories文件 (spi扩展机制加载)
//spring容器刷新之前初始化,调用实现类initialize方法
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//加载ApplicationListener实现类,从缓存中获取后实例化
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringApplication构造函数主要是加载应用监听类和上下文初始化类,通过查找类路径下文件解析并获取其配置的实现类

下面来看run方法函数

//
public ConfigurableApplicationContext run(String… args) {
//记录启动运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//一: 创建监听实现类EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//二:准备应用环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//创建打印类
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);
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;
}
下面分步骤来了解启动过程。

一:创建监听实现类EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);

//获取运行监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class[] types = new Class[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

private Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object… args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//类加载器加载类路径下 META-INF/spring.factories 文件中的限定类名
Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//加载及创建实例
List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
spring.factories文件中配置的监听类:

Run Listeners

org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
listeners.starting();

EventPublishingRunListener类:

private final SimpleApplicationEventMulticaster initialMulticaster;

public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener listener : application.getListeners()) {
//加入spring.factories文件中配置的监听类
this.initialMulticaster.addApplicationListener(listener);
}
}

public void starting() {
//发布应用启动事件
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
SimpleApplicationEventMulticaster类:
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
//执行监听器事件
listener.onApplicationEvent(event);
…省略异常代码
}
二:准备应用环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
注:
bindToSpringApplication()
:将环境绑定到SpringApplication类,下文2.3已说明本次环境依赖spring cloud jar包,所以配置文件加载优先级:

bootstrap.yml
application.yml 配置文件属性通过反射注入SpringApplication对象属性值。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 2.1 创建环境对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
//2.2 对属性源和配置文件进行配置
configureEnvironment(environment, applicationArguments.getSourceArgs());
//将属性源支持附加到指定的环境
ConfigurationPropertySources.attach(environment);
//2.3 发布环境已准备事件
listeners.environmentPrepared(environment);
//将环境绑定到spring 应用
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
2.1 创建环境对象
ConfigurableEnvironment environment = getOrCreateEnvironment();

StandardServletEnvironment创建对象时会加入一组属性源 属性源名称按添加顺序排序:

servletConfigInitParams:Servlet配置初始化参数
servletContextInitParams:Servlet上下文初始化参数
systemProperties:系统环境
systemEnvironment:JVM系统属性
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
//SpringApplication初始化时已被设置为servlet类型,此时创建标准servlet环境对象
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
//标准环境对象,spring cloud初始化时会创建
return new StandardEnvironment();
}
}
2.2 对属性源和配置文件进行配置
configureEnvironment(environment, applicationArguments.getSourceArgs());

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
//创建类型转换的服务接口,添加格式化程序和转换器
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
//模板方法,可重写
//从环境对象中获取属性源进行配置,如设置命令行属性源,用于获取命令行设置的参数
configurePropertySources(environment, args);
//配置默认的profiles环境标识
configureProfiles(environment, args);
}
2.3 发布环境已准备事件
listeners.environmentPrepared(environment);

本项目用到了
spring cloud
的部分组件,如spring cloud config, ribbon组件等,有依赖spring-cloud-context.jar, 所以在SpringApplication初始化时会加载
BootstrapApplicationListener
监听器,此监听器用于创建spring cloud应用上下文

根据事件类型获取到的监听器:

2.3.1 分析BootstrapApplicationListener监听器
BootstrapApplicationListener类:
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
//禁用bootstrap配置过程
if (!environment.getProperty(“spring.cloud.bootstrap.enabled”, Boolean.class,
true)) {
return;
}
//不监听bootstrap上下文事件
if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
return;
}
ConfigurableApplicationContext context = null;
//读取环境配置属性,默认bootstarp
String configName = environment
.resolvePlaceholders(“${spring.cloud.bootstrap.name:bootstrap}”);
for (ApplicationContextInitializer initializer : event.getSpringApplication()
.getInitializers()) {
//ParentContextApplicationContextInitializer:设置父上下文,添加应用监听
if (initializer instanceof ParentContextApplicationContextInitializer) {
context = findBootstrapContext(
(ParentContextApplicationContextInitializer) initializer,
configName);
}
}
if (context == null) {
//重点:创建bootstrap上下文,初始化spring cloud环境对象
context = bootstrapServiceContext(environment, event.getSpringApplication(),
configName);
//监听启动失败事件,用于关闭bootstartp上下文,销毁bean
event.getSpringApplication()
.addListeners(new CloseContextOnFailureApplicationListener(context));
}

apply(context, event.getSpringApplication(), environment);
}
private ConfigurableApplicationContext bootstrapServiceContext(
ConfigurableEnvironment environment, final SpringApplication application,
String configName) {
//新建bootstrap环境对象
StandardEnvironment bootstrapEnvironment = new StandardEnvironment();
……

Map<String, Object> bootstrapMap = new HashMap<>();
//添加bootstarp配置名

bootstrapMap.put(“spring.config.name”, configName);
bootstrapMap.put(“spring.main.web-application-type”, “none”);

SpringApplicationBuilder builder = new SpringApplicationBuilder()
.profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)
.environment(bootstrapEnvironment)
.registerShutdownHook(false).logStartupInfo(false)
//NONE: 获取非web应用程序的上下文环境,不会启动web容器(tomcat)
.web(WebApplicationType.NONE);
final SpringApplication builderApplication = builder.application();
……

//将BootstrapImportSelector类注入容器并获取BootstrapConfiguration 配置类

builder.sources(BootstrapImportSelectorConfiguration.class);
//创建bootstarp上下文
final ConfigurableApplicationContext context = builder.run();
//设置上下文id
context.setId(“bootstrap”);
//将bootstrap上下文设置为当前应用的父上下文
addAncestorInitializer(application, context);
bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);
//合并属性源
mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);
return context;
}
2.3.2 分析ConfigFileApplicationListener监听器
此监听器用于加载配置文件,在创建bootstrap上下文中会加载配置的bootstrap.yml文件。

builder.run()调用后会在prepareEnvironment 方法中执行环境已准备事件,获取到ConfigFileApplicationListener监听器,在onApplicationEvent方法中由于当前是环境已准备事件,调用onApplicationEnvironmentPreparedEvent方法将其自身加入环境配置的集合。

ConfigFileApplicationListener类
//默认搜索路径
private static final String DEFAULT_SEARCH_LOCATIONS = “classpath:/,classpath:/config/,file:./,file:./config/”;

@Override
public void onApplicationEvent(ApplicationEvent event) {
//环境已准备事件
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
//获取环境变量扩展类
List postProcessors = loadPostProcessors();
//自身加入集合执行
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {

RandomValuePropertySource.addToEnvironment(environment);
//加载配置文件
new Loader(environment, resourceLoader).load();
}
}
创建内部类Loader,初始化构造函数时通过spi方法加载不同的配置实现类。

YamlPropertySourceLoader
:加载.yml、.yaml格式的文件
PropertiesPropertySourceLoader
:加载.properties格式的文件
具体路径:默认搜索路径 + 配置属性名 + 文件扩展名 例子:classpath:/bootstarp.yml

private class Loader {

void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
//初始化默认属性值 null,default
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
//第一次进入,profile等于空,此时是spring cloud环境属性,根据上面的搜索路径,会找到bootstrap.yml文件,读取文件中配置的spring.profiles.active属性,读到后加入profiles属性集合,移除掉default属性值。
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}

private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {

getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith(“/”);
Set names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}

private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
  ...

Set processed = new HashSet<>();
//properties,ymal 两个文件扩展实现类
for (PropertySourceLoader loader : this.propertySourceLoaders) {
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
loadForFileExtension(loader, location + name, “.” + fileExtension, profile, filterFactory,
consumer);
}
}
}
}
}
后续会详细出spring cloud上下文加载流程, spring cloud依赖组件多,篇幅有限。此处就分析到这。下面继续分析spring boot后续流程。

三:创建应用程序上下文
context = createApplicationContext();

Class.forName()初始化AnnotationConfigServletWebServerApplicationContext类,执行静态代码块,此时没有实例化该类。通过instantiateClass方法中调用newInstance方法实例化,此时会调用父类GenericApplicationContext构造函数,执行DefaultListableBeanFactory类的实例化操作。DefaultListableBeanFactory是默认IOC容器实现类, BeanFactory是顶层容器抽象接口,为具体的容器实现类提供了基本的规范。

protected ConfigurableApplicationContext createApplicationContext() {
Class contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
//创建servlet web上下文,后面会启动tomcat
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
//spring cloud创建上下文对象
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
“Unable create a default ApplicationContext, please specify an ApplicationContextClass”, ex);
}
}
//反射创建对象
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
四:准备应用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);

SpringApplication类:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置上下文环境
context.setEnvironment(environment);
//应用后置处理,设置服务转换接口
postProcessApplicationContext(context);
//初始化应用上下文
applyInitializers(context);
//发布上下文已初始化事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//注册单例bean
beanFactory.registerSingleton(“springApplicationArguments”, applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton(“springBootBanner”, printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
//设置是否允许同名的bean注册,默认false,重复注册抛异常 true:自动覆盖前者
//上文有讲,如果bootstrap.yml 和 application.yml都配置了allowBeanDefinitionOverriding属性,会读取bootstrap.yml文件的值,忽略后面的文件
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set

声明:来自阿飞技术,仅代表创作者观点。链接:https://eyangzhen.com/2879.html

阿飞技术的头像阿飞技术

相关推荐

关注我们
关注我们
购买服务
购买服务
返回顶部