在上一章咱们剖析了SpingBoot启动流程中实例化SpingApplication的过程。
return new SpringApplication(primarySources).run(args);
这篇文章咱么说下run()
办法开始之后都做了那些事件。
持续往下跟着源码进入到run()
这个是比拟外围的一个办法了
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
// 计时器开始
stopWatch.start();
// 创立启动上下文对象
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 配置Handless模式,是在短少显示屏、键盘或鼠标时的系统配置
// 默认为true
configureHeadlessProperty();
//获取并启动监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听器
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 筹备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 疏忽配置的bean
configureIgnoreBeanInfo(environment);
// 打印banner,就是启动的时候在控制台的spring图案
Banner printedBanner = printBanner(environment);
// 创立容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 筹备利用上下文(spring容器前置解决)
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新容器
refreshContext(context);
// 刷新容器后的扩大接口(spring容器后置解决)
afterRefresh(context, applicationArguments);
// 完结计时器并打印,这就是咱们启动后console的显示的工夫
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 公布监听利用上下文启动实现(收回启动完结事件)
listeners.started(context);
// 执行runner
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 异样解决,如果run过程产生异样
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 监听利用上下文运行中
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
// 返回最终构建的容器对象
return context;
}
接下来就对下面的关键步骤一一解释
1. 获取所有的监听器
这段代码咱们比拟相熟了,上一篇咱么具体介绍过,它的次要作用就是去META-INFO/spring.factories
中加载配置SpringApplicationRunListener的监听器如下
显然只有一个事件公布监听器类,拿到了EventPublishingRunListener
启动事件公布监听器,下一步就是开始启动了listeners.starting()
;咱们往下跟源码看
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
this.initialMulticaster
.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
启动的时候实际上是又创立了一个ApplicationStartingEvent
对象,其实就是监听利用启动事件。
其中 initialMulticaster
是一个SimpleApplicationEventMuticaster
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
// 获取线程池,为每个监听事件创立一个线程
Executor executor = this.getTaskExecutor();
// 依据ApplicationStartingEvent事件类型找到对应的监听器,并迭代
Iterator var5 = this.getApplicationListeners(event, type).iterator();
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
//
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
2.筹备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 这里咱们退出了web依赖所以是一个servlet容器
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 环境筹备实现
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
configureAdditionalProfiles(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
因为咱们是增加了web的依赖 getOrCreateEnvironment()
返回的是一个standardservletEnviroment
规范的servlet环境
2.1 配置环境
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
// 嵌入式的转换器
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// 配置属性资源文件
configurePropertySources(environment, args);
// 配置文件
configureProfiles(environment, args);
}
利用嵌入的转换器ApplicationConversionService
public static void configure(FormatterRegistry registry) {
DefaultConversionService.addDefaultConverters(registry);
DefaultFormattingConversionService.addDefaultFormatters(registry);
// 格局转换
addApplicationFormatters(registry);
// 类型转换
addApplicationConverters(registry);
}
===============格局转换=================
public static void addApplicationFormatters(FormatterRegistry registry) {
registry.addFormatter(new CharArrayFormatter());
registry.addFormatter(new InetAddressFormatter());
registry.addFormatter(new IsoOffsetFormatter());
}
========================类型转换===================
public static void addApplicationConverters(ConverterRegistry registry) {
addDelimitedStringConverters(registry);
registry.addConverter(new StringToDurationConverter());
registry.addConverter(new DurationToStringConverter());
registry.addConverter(new NumberToDurationConverter());
registry.addConverter(new DurationToNumberConverter());
registry.addConverter(new StringToPeriodConverter());
registry.addConverter(new PeriodToStringConverter());
registry.addConverter(new NumberToPeriodConverter());
registry.addConverter(new StringToDataSizeConverter());
registry.addConverter(new NumberToDataSizeConverter());
registry.addConverter(new StringToFileConverter());
registry.addConverter(new InputStreamSourceToByteArrayConverter());
registry.addConverterFactory(new LenientStringToEnumConverterFactory());
registry.addConverterFactory(new LenientBooleanToEnumConverterFactory());
if (registry instanceof ConversionService) {
addApplicationConverters(registry, (ConversionService) registry);
}
}
2.2 环境筹备实现
同下面启动监听事件,这次的环境筹备也是同样的代码
@Override
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(
// 创立一个应用环境筹备事件对象
new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
debug进去之后代码跟AppLicationstrigevent 事件对象是一样的。不再赘述。
不过这里是7个监听器对象
3.配置疏忽的bean
configureIgnoreBeanInfo(environment);
4.打印banner
这是SpringBoot默认的启动时的图标
Banner printedBanner = printBanner(environment);
这个是能够自定义的,也能够是图篇或是文本文件中的图形
5.创立容器
紧接着上一篇,接下来就是创立容器
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
6.筹备利用上下文
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置环境参数
context.setEnvironment(environment);
// 设置后处理利用上下文
postProcessApplicationContext(context);
//把从spring.factories中加载的org.springframework.bt.context.ConfigurationwarningsApplicationContextIitiaLizer,进行初始化操作
applyInitializers(context);
//EventPubLishingRunListener公布利用上下文事件
listeners.contextPrepared(context);
// 打印启动日志
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
//注册一个字是springAppLicationArguments单例的bean
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources 获取所有资源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 创立BeanDefinitionLoader加载器加载注册所有的资源
load(context, sources.toArray(new Object[0]));
// 同之前,公布利用上下文 加载事件
listeners.contextLoaded(context);
}
7.刷新利用上下文
刷新利用上下文就进入了spring的源码了
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
//筹备刷新上下文
this.prepareRefresh();
// Tetl the subclass to refresh the internal bean facto
// 告诉子类刷新外部工厂
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 筹备Bean工厂
this.prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in contex t subc lasses.
// 容许在上下文子类中对bean工厂进行后处理。
// Invoke factory processors registered as beans in the context,
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
this.invokeBeanFactoryPostProcessors(beanFactory);
// 注册后置处理器。
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 初始化信息源
this.initMessageSource();
// 初始化上下文事件公布器
this.initApplicationEventMulticaster();
// 初始化其余自定义bean
this.onRefresh();
// 注册监听器
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
//实现刷新,清缓存,初始化生命周期,事件公布等
this.finishRefresh();
} catch (BeansException var10) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
}
// 销毁bean
this.destroyBeans();
// Reset 'active'flag.
this.cancelRefresh(var10);
throw var10;
} finally {
this.resetCommonCaches();
contextRefresh.end();
}
}
}
刷新的代码有点深,也是在这时创立了Tomcat对象,这也是SpringBoot
一键启动web工程的要害
创立了Tomcat对象,并设置参数
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
// 返回TomcatWebServer服务
return getTomcatWebServer(tomcat);
}
8.刷新后处理
afterReftesh();
//是个一空实现,留着前期扩大
/**
* Called after the context has been refreshed.
* @param context the application context
* @param args the application arguments
*/
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
9.公布监听利用启动事件
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
这里是调用context.publishEvent()办法,公布利用启动事件ApplicationStartedEvent.
10.执行Runner
获取所有的ApplicationRuner和CommandLineRunner来初始化一些参数,callRuner(是一个回调函数)
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
11.公布上下文筹备实现的事件
listeners.running(context);
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC);
}
这段代码看上去似成相识,后面有很多相似的代码,不同的是这里上下文筹备实现之后公布了一个ApplicationReadyEvent事件,申明一下利用上下文筹备实现。
小结
这篇次要是介绍了SpringBoot启动过程中run()
的这个过程。从中咱们也能够发现一些十分好的编码习惯,大家能够在日常的工作中从模拟到内化,缓缓变成本人的货色。
如果你最近正在学习SpringBoot,能够参考我的集体网站:程序员波特,目前曾经更新了SpringBoot2.x,SpringBoot3.x的系列学习教程,心愿能够帮忙到你。
发表回复