关于springboot:SpringBoot启动流程原理解析二
<article class=“article fmt article-content”><blockquote>在上一章咱们剖析了SpingBoot启动流程中实例化SpingApplication的过程。</blockquote><p><code>return new SpringApplication(primarySources).run(args);</code><br/>这篇文章咱么说下<code>run()</code>办法开始之后都做了那些事件。<br/><br/>持续往下跟着源码进入到<code>run()</code>这个是比拟外围的一个办法了<br/></p><pre><code> 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; }</code></pre><p>接下来就对下面的关键步骤一一解释</p><h3>1. 获取所有的监听器</h3><p><br/>这段代码咱们比拟相熟了,上一篇咱么具体介绍过,它的次要作用就是去<code>META-INFO/spring.factories</code> 中加载配置SpringApplicationRunListener的监听器如下<br/><br/>显然只有一个事件公布监听器类,拿到了<code>EventPublishingRunListener</code>启动事件公布监听器,下一步就是开始启动了<code>listeners.starting()</code>;咱们往下跟源码看</p><pre><code> @Override public void starting(ConfigurableBootstrapContext bootstrapContext) { this.initialMulticaster .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args)); }</code></pre><p>启动的时候实际上是又创立了一个<code>ApplicationStartingEvent</code>对象,其实就是监听利用启动事件。<br/>其中 <code>initialMulticaster</code>是一个<code>SimpleApplicationEventMuticaster</code></p><pre><code> 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); } } }</code></pre><h3>2.筹备环境</h3><p><code>ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);</code></p><pre><code> 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; }</code></pre><p>因为咱们是增加了web的依赖 <code>getOrCreateEnvironment()</code>返回的是一个<code>standardservletEnviroment</code> 规范的servlet环境</p><h4>2.1 配置环境</h4><pre><code> protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { if (this.addConversionService) { // 嵌入式的转换器 ConversionService conversionService = ApplicationConversionService.getSharedInstance(); environment.setConversionService((ConfigurableConversionService) conversionService); } // 配置属性资源文件 configurePropertySources(environment, args); // 配置文件 configureProfiles(environment, args); }</code></pre><p>利用嵌入的转换器<code>ApplicationConversionService</code></p><pre><code> 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); } } </code></pre><h4>2.2 环境筹备实现</h4><blockquote>同下面启动监听事件,这次的环境筹备也是同样的代码</blockquote><pre><code> @Override public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent( // 创立一个应用环境筹备事件对象 new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment)); }</code></pre><p>debug进去之后代码跟AppLicationstrigevent 事件对象是一样的。不再赘述。<br/>不过这里是7个监听器对象</p><h4>3.配置疏忽的bean</h4><p><code>configureIgnoreBeanInfo(environment);</code></p><h4>4.打印banner</h4><blockquote>这是SpringBoot默认的启动时的图标<br/><code>Banner printedBanner = printBanner(environment);</code><br/><br/>这个是能够自定义的,也能够是图篇或是文本文件中的图形</blockquote><h3>5.创立容器</h3><p>紧接着上一篇,接下来就是创立容器</p><pre><code> protected ConfigurableApplicationContext createApplicationContext() { return this.applicationContextFactory.create(this.webApplicationType); }</code></pre><h3>6.筹备利用上下文</h3><pre><code> 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); }</code></pre><h3>7.刷新利用上下文</h3><blockquote>刷新利用上下文就进入了spring的源码了</blockquote><pre><code> 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(); } } }</code></pre><p>刷新的代码有点深,也是在这时创立了Tomcat对象,这也是<code>SpringBoot</code><strong> 一键启动</strong>web工程的要害<br/></p><p><br/>创立了Tomcat对象,并设置参数</p><pre><code> @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); }</code></pre><p></p><h3>8.刷新后处理</h3><blockquote><code>afterReftesh();</code> //是个一空实现,留着前期扩大</blockquote><pre><code> /** * Called after the context has been refreshed. * @param context the application context * @param args the application arguments */ protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { }</code></pre><h3>9.公布监听利用启动事件</h3><pre><code> @Override public void started(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context)); AvailabilityChangeEvent.publish(context, LivenessState.CORRECT); }</code></pre><blockquote>这里是调用context.publishEvent()办法,公布利用启动事件ApplicationStartedEvent.</blockquote><h3>10.执行Runner</h3><blockquote>获取所有的ApplicationRuner和CommandLineRunner来初始化一些参数,callRuner(是一个回调函数)</blockquote><pre><code> 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); } } }</code></pre><h3>11.公布上下文筹备实现的事件</h3><p><code>listeners.running(context);</code></p><pre><code> @Override public void running(ConfigurableApplicationContext context) { context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context)); AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC); }</code></pre><p>这段代码看上去似成相识,后面有很多相似的代码,不同的是这里上下文筹备实现之后公布了一个ApplicationReadyEvent事件,申明一下利用上下文筹备实现。</p><h3>小结</h3><p>这篇次要是介绍了SpringBoot启动过程中<code>run()</code>的这个过程。从中咱们也能够发现一些十分好的编码习惯,大家能够在日常的工作中从模拟到内化,缓缓变成本人的货色。</p><blockquote>如果你最近正在学习SpringBoot,能够参考我的集体网站:程序员波特,目前曾经更新了SpringBoot2.x,SpringBoot3.x的系列学习教程,心愿能够帮忙到你。</blockquote></article> ...