<article class=“article fmt article-content”><p>先来一段经典的SpringBoot启动代码</p><pre><code class=“java”>@SpringBootApplicationpublic class SpringbootDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringbootDemoApplication.class, args); }}</code></pre><p>SpringBoot应用程序的启动是调用SpringApplication的动态run办法开始的,咱们看一下其源代码</p><pre><code class=“java”>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);}</code></pre><p>能够看到,启动程序是先将启动类,即示例代码中的SpringbootDemoApplication类作为参数,结构出一个SpringApplication实例,再调用这个SpringApplication实例的run办法进行启动</p><h2>getSpringFactoriesInstances</h2><p>这里咱们先理解一下SpringBoot中很常见的getSpringFactoriesInstances办法</p><pre><code class=“java”>// 从各个jar包中的spring.factories中获取到类型为type的类,并将其实例化private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {});}/** * 从各个jar包中的spring.factories中获取到类型为type的类,并调用其结构函数参数类型为parameterTypes的函数进行初始化 * type: 从各个jar包中的spring.factories中获取到类型为type的类 * parameterTypes: 构造函数的参数类型列表 * args: 构造函数的参数列表 /private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object… args) { ClassLoader classLoader = getClassLoader(); // 调用SpringFactoriesLoader,找到其中类型为type的类,返回这些类的全限定名 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 结构下面这些类的实例 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 依照注解排个序,即@Order AnnotationAwareOrderComparator.sort(instances); // 返回这些实例 return instances;}</code></pre><p>下面代码中,通过SpringFactoriesLoader加载类型为type的类。这里咱们不深入分析了,简略来说,SpringFactoriesLoader就是加载类门路下,所有的META-INF/spring.factories文件,这些文件中是一个properties文件,其中定义了各个类型(即type)及其实现子类,以下是一个文件实例<br/>key就是type的全限定名,value就是咱们返回的类名的汇合<br/><br/></p><h2>SpringApplication构造方法</h2><pre><code class=“java”>public SpringApplication(Class<?>… primarySources) { this(null, primarySources);}/** resourceLoader 资源加载器,默认为空* primarySources 咱们的启动类的class*/@SuppressWarnings({ “unchecked”, “rawtypes” })public SpringApplication(ResourceLoader resourceLoader, Class<?>… primarySources) { // 资源加载器,默认为空 this.resourceLoader = resourceLoader; Assert.notNull(primarySources, “PrimarySources must not be null”); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 1. 检测Web应用程序类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 2. 加载疏导上下文的初始化器,疏导上下文也是一个容器,次要利用在SpringBoot启动阶段疏导应用程序上下文启动 this.bootstrapRegistryInitializers = new ArrayList<>( getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); // 3. 加载应用程序初始化器 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 4. 加载监听器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 5. 设置主启动类 this.mainApplicationClass = deduceMainApplicationClass();}</code></pre><p>咱们来挨个剖析其中的代码</p><h3>检测Web应用程序类型</h3><p>检测Web应用程序类型是由WebApplicationType实现的,以下是WebApplicationType的源代码</p><pre><code class=“java”>public enum WebApplicationType { // 阐明不是Web应用程序,不应该启动Web服务器 NONE, // 阐明应用程序是基于Servlet启动的,会启动内嵌的Web服务器 SERVLET, // 阐明应用程序时基于响应式的web应用程序,会启动内嵌的响应式Web服务器 REACTIVE; private static final String[] SERVLET_INDICATOR_CLASSES = { “javax.servlet.Servlet”, “org.springframework.web.context.ConfigurableWebApplicationContext” }; private static final String WEBMVC_INDICATOR_CLASS = “org.springframework.web.servlet.DispatcherServlet”; private static final String WEBFLUX_INDICATOR_CLASS = “org.springframework.web.reactive.DispatcherHandler”; private static final String JERSEY_INDICATOR_CLASS = “org.glassfish.jersey.servlet.ServletContainer”; static WebApplicationType deduceFromClasspath() { // 如果类门路下有DispatcherHandler并且没有DispatcherServlet,也没有ServletContainer,阐明是响应式Web应用程序 if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } // 如果类门路下没有Setvlet相干类,则阐明不是Web应用程序 for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } // 其余状况示意是一般的Servlet的Web应用程序 return WebApplicationType.SERVLET; }}</code></pre><h3>加载疏导上下文的初始化器</h3><p>接下来是加载疏导上下文的初始化器,即BootstrapRegistryInitializer的实例,咱们能够看一下BootstrapRegistryInitializer的源代码</p><pre><code class=“java”>// BootstrapRegistry的回调接口,在BootstrapRegistry应用之前对BootstrapRegistry进行解决@FunctionalInterfacepublic interface BootstrapRegistryInitializer { void initialize(BootstrapRegistry registry);}</code></pre><p>在这里就不得不先提到疏导上下文BootstrapContext,它是一个小容器,专门在SpringBoot启动过程中疏导应用程序容器启动,而BootstrapContext目前只有惟一一个实现类DefaultBootstrapContext,而它同时实现了BootstrapRegistry。BootsstrapRegistryInitializer就是对这个DefaultBootstrapContext做解决的<br/><br/><br/>而默认状况下,SpringBoot并没有定义任何的BootstrapRegistryInitializer</p><h3>加载应用程序初始化器</h3><p>应用程序初始化器,对应用程序上下文进行初始化解决的</p><pre><code class=“java”>/** * ConfigurableApplicationContext的回调接口,会在refresh办法调用之前对ApplicationContext进行解决 * 个别用于对ApplicationContext进行一些初始化工作,比方注册一个属性源或者激活某个profile * * ApplicationContextInitializer会依照Ordered接口或者@Order注解定义的优先级进行排序 /@FunctionalInterfacepublic interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { void initialize(C applicationContext);}</code></pre><p>以下是SpringBoot默认提供的应用程序上下文的初始化器<br/><br/></p><h3>加载监听器</h3><pre><code class=“java”>// 应用程序监听器,监听ApplicationEvent接口。是基于观察者模式创立的接口@FunctionalInterfacepublic interface ApplicationListener<E extends ApplicationEvent> extends EventListener { void onApplicationEvent(E event); /* * 静态方法,为音讯类型T创立一个监听器 / static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) { return event -> consumer.accept(event.getPayload()); }}</code></pre><p>咱们再看看SpringBoot默认提供了哪些利用监听器<br/><br/></p><h3>设置主启动类</h3><pre><code class=“java”>private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if (“main”.equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null;}</code></pre><p>这个代码十分有意思,它是手动创立了一个异样,而后追踪异样堆栈信息,找到main办法所在的类,它就是启动类</p><h2>run办法解析</h2><p>看完了SpringApplication的构造方法逻辑,咱们接下来看看run办法的实现</p><pre><code class=“java”>// 创立应用程序的ApplicationContext,并进行刷新启动public ConfigurableApplicationContext run(String… args) { long startTime = System.nanoTime(); // 1. 创立疏导上下文 DefaultBootstrapContext bootstrapContext = createBootstrapContext(); ConfigurableApplicationContext context = null; // 配置无头模式参数,不做剖析 configureHeadlessProperty(); // 2. 创立SpringBootRunListeners,即启动过程中的监听器。在启动过程中会触发这些监听器 SpringApplicationRunListeners listeners = getRunListeners(args); // 触发应用程序开始启动的事件 listeners.starting(bootstrapContext, this.mainApplicationClass); try { // 解析命令行参数,封装到ApplicationArguments ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 3. 筹备好Environment ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); configureIgnoreBeanInfo(environment); // 输入banner,不重要,疏忽 Banner printedBanner = printBanner(environment); // 4. 创立应用程序上下文 context = createApplicationContext(); context.setApplicationStartup(this.applicationStartup); // 5. 筹备好应用程序上下文 prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 6. 刷新应用程序上下文 refreshContext(context); // 刷新后的解决,默认没有实现,这个是交给子类实现的 afterRefresh(context, applicationArguments); Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup); } // 触发启动结束的事件 listeners.started(context, timeTakenToStartup); // 7. 调用SpringRunner callRunners(context, applicationArguments); } catch (Throwable ex) { // 解决启动失败 handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); // 触发筹备结束的事件 listeners.ready(context, timeTakenToReady); } catch (Throwable ex) { // 解决启动失败 handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context;}</code></pre><p></p><h3>创立疏导上下文createBootstrapContext</h3><p>SpringApplication会调用createBootstrapContext办法创立疏导上下文</p><pre><code class=“java”>private DefaultBootstrapContext createBootstrapContext() { DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); return bootstrapContext;}</code></pre><p>这里就能够看到,在SpringApplication构造方法中的BootstrapRegistryInitializers就会利用到DefaultBootstrapContext中。这也是SpringBoot提供的扩大点之一</p><h4>以后扩大点图谱</h4><p></p><h3>创立SpringApplicationRunListeners</h3><pre><code class=“java”>private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);}</code></pre><h4>SpringApplicationRunListener</h4><p>SpringApplicationRunListener监听run办法的各个阶段,在不同的阶段监听不同的事件</p><pre><code class=“java”>// 对run办法中的各个阶段进行监听触发,其实现类必须有一个公共结构参数,其承受的参数为// SpringApplication和[]String,前者为SpringApplication的实例,后者为启动应用程序输出的参数public interface SpringApplicationRunListener { /* * 在run办法开始启动时触发 * @param bootstrapContext 疏导上下文 / default void starting(ConfigurableBootstrapContext bootstrapContext) { } /* * 当Environment筹备结束时触发,此时应用程序上下文ApplicationContext还没有被创立 * 触发办法为prepareEnvironment * @param bootstrapContext 疏导上下文 * @param environment Environment实例 / default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { } /* * 在应用程序上下文ApplicationContext创立并筹备实现时触发 * 触发办法为prepareContext * @param context 应用程序上下文 / default void contextPrepared(ConfigurableApplicationContext context) { } /* * 在应用程序上下文ApplicationContext载入初始化bean之后触发,此时还未触发上下文的refresh办法 * 触发办法文prepareContext * @param context 应用程序上下文 / default void contextLoaded(ConfigurableApplicationContext context) { } /* * 在应用程序上下文刷新并启动实现之后触发,此时CommandLineRunner和ApplicationRunner还未触发 * 在run办法内触发 * @param context 应用程序上下文 * @param timeTaken 从应用程序启动至触发started事件触发破费的工夫,可能为null * @since 2.6.0 / default void started(ConfigurableApplicationContext context, Duration timeTaken) { started(context); } /* * 晚期的started办法,已弃用 / @Deprecated default void started(ConfigurableApplicationContext context) { } /* * 在应用程序启动并刷新实现,并且所有的CommandLineRunner和ApplicationRunner都运行实现之后触发 * 在run办法内触发 * @param context 应用程序上下文 * @param timeTaken 从应用程序启动至触发ready事件触发破费的事件,可能为null * @since 2.6.0 / default void ready(ConfigurableApplicationContext context, Duration timeTaken) { running(context); } /* * 晚期的ready办法,已弃用 / @Deprecated default void running(ConfigurableApplicationContext context) { } /* * 当启动利用失败时触发 * @param context 应用程序上下文,可能为null * @param exception 导致启动失败的异样 * @since 2.0.0 / default void failed(ConfigurableApplicationContext context, Throwable exception) { }}</code></pre><p>接下来看看SpringBoot默认提供的SpringApplicationRunListener<br/><br/></p><h4>SpringApplicationRunListeners</h4><p>SpringApplicationRunListeners是一个封装类,其中封装了一个SpringApplicationRunListener的列表,当触发某个事件是,就挨个调用其中的SpringApplicationRunListener的对应办法</p><pre><code class=“java”>class SpringApplicationRunListeners { private final Log log; private final List<SpringApplicationRunListener> listeners; private final ApplicationStartup applicationStartup; SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners, ApplicationStartup applicationStartup) { this.log = log; this.listeners = new ArrayList<>(listeners); this.applicationStartup = applicationStartup; } void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) { doWithListeners(“spring.boot.application.starting”, (listener) -> listener.starting(bootstrapContext), (step) -> { if (mainApplicationClass != null) { step.tag(“mainApplicationClass”, mainApplicationClass.getName()); } }); } void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { doWithListeners(“spring.boot.application.environment-prepared”, (listener) -> listener.environmentPrepared(bootstrapContext, environment)); } void contextPrepared(ConfigurableApplicationContext context) { doWithListeners(“spring.boot.application.context-prepared”, (listener) -> listener.contextPrepared(context)); } void contextLoaded(ConfigurableApplicationContext context) { doWithListeners(“spring.boot.application.context-loaded”, (listener) -> listener.contextLoaded(context)); } void started(ConfigurableApplicationContext context, Duration timeTaken) { doWithListeners(“spring.boot.application.started”, (listener) -> listener.started(context, timeTaken)); } void ready(ConfigurableApplicationContext context, Duration timeTaken) { doWithListeners(“spring.boot.application.ready”, (listener) -> listener.ready(context, timeTaken)); } void failed(ConfigurableApplicationContext context, Throwable exception) { doWithListeners(“spring.boot.application.failed”, (listener) -> callFailedListener(listener, context, exception), (step) -> { step.tag(“exception”, exception.getClass().toString()); step.tag(“message”, exception.getMessage()); }); } private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context, Throwable exception) { try { listener.failed(context, exception); } catch (Throwable ex) { if (exception == null) { ReflectionUtils.rethrowRuntimeException(ex); } if (this.log.isDebugEnabled()) { this.log.error(“Error handling failed”, ex); } else { String message = ex.getMessage(); message = (message != null) ? message : “no error message”; this.log.warn(“Error handling failed (” + message + “)”); } } } private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) { doWithListeners(stepName, listenerAction, null); } private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) { StartupStep step = this.applicationStartup.start(stepName); // 调用每个办法 this.listeners.forEach(listenerAction); if (stepAction != null) { stepAction.accept(step); } step.end(); }}</code></pre><h4>EventPublishingRunListener</h4><p>SpringBoot默认应用EventPublishingRunListener作为run办法的监听者,咱们来看看其源代码</p><pre><code class=“java”>/* * SpringApplicationRunListener 的实现类,它次要是依赖Spring的事件散发机制来触发事件 /public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { private final SpringApplication application; private final String[] args; 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()) { this.initialMulticaster.addApplicationListener(listener); } } @Override public int getOrder() { return 0; } @Override public void starting(ConfigurableBootstrapContext bootstrapContext) { this.initialMulticaster .multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args)); } @Override public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent( new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment)); } @Override public void contextPrepared(ConfigurableApplicationContext context) { this.initialMulticaster .multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context)); } @Override public void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<?> listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context)); } @Override public void started(ConfigurableApplicationContext context, Duration timeTaken) { context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context, timeTaken)); AvailabilityChangeEvent.publish(context, LivenessState.CORRECT); } @Override public void ready(ConfigurableApplicationContext context, Duration timeTaken) { context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context, timeTaken)); AvailabilityChangeEvent.publish(context, ReadinessState.ACCEPTING_TRAFFIC); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception); if (context != null && context.isActive()) { // Listeners have been registered to the application context so we should // use it at this point if we can context.publishEvent(event); } else { // An inactive context may not have a multicaster so we use our multicaster to // call all the context’s listeners instead if (context instanceof AbstractApplicationContext) { for (ApplicationListener<?> listener : ((AbstractApplicationContext) context) .getApplicationListeners()) { this.initialMulticaster.addApplicationListener(listener); } } this.initialMulticaster.setErrorHandler(new LoggingErrorHandler()); this.initialMulticaster.multicastEvent(event); } } private static class LoggingErrorHandler implements ErrorHandler { private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class); @Override public void handleError(Throwable throwable) { logger.warn(“Error calling ApplicationEventListener”, throwable); } }}</code></pre><p>从代码中能够看到,EventPublishingRunListener会从SpringApplication中获取其Listener,即后面咱们在构造方法中看到的ApplicationListener实例,在触发事件时,就是利用Spring的事件机制公布事件,触发ApplicationListener进行触发<br/>这里须要留神的是,ApplicationListener的起源是spring.factories,而不是咱们平时应用的@EventListener,也就是说,如果不写入到spring.factories,那么ApplicationListener就不会呈现在这里的EventPublishingRunListener中</p><h4>以后扩大点图谱</h4><p></p><h3>筹备好Environment prepareEnvironment</h3><p>先看源码</p><pre><code class=“java”>private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // 1. 创立一个Environment实例 ConfigurableEnvironment environment = getOrCreateEnvironment(); // 2. 对Environment进行配置 configureEnvironment(environment, applicationArguments.getSourceArgs()); // 对environment减少configurationProperty的属性源 ConfigurationPropertySources.attach(environment); // 3. 触发监听SpringApplicationRunListener的environmentPrepared事件 listeners.environmentPrepared(bootstrapContext, environment); // 将名为defaultProperties的属性源挪动到最初 DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty(“spring.main.environment-prefix”), “Environment prefix cannot be set via properties.”); // 将environment的属性设置到SpringApplication中 bindToSpringApplication(environment); // 依据状况对Environment进行一次转换 if (!this.isCustomEnvironment) { EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader()); environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment;}</code></pre><h4>创立Environment实例,getOrCreateEnvironment</h4><pre><code class=“java”>private ConfigurableEnvironment getOrCreateEnvironment() { // 如果以后environment不为空,间接返回 if (this.environment != null) { return this.environment; } // 依据利用类型创立Environment ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(this.webApplicationType); // 如果用户本人通过编程形式定制了applicationContextFactory,且其自定义的applicationContextFactory没有胜利创立Environment // 则采纳默认的形式创立一个environment if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) { environment = ApplicationContextFactory.DEFAULT.createEnvironment(this.webApplicationType); } // 反正最初必定会返回一个不为空的Environment return (environment != null) ? environment : new ApplicationEnvironment();}</code></pre><p>这里会应用成员属性applicationContextFactory创立Environment,其是一个ApplicationContextFactory接口类型。默认状况下SpringApplication中applicationContextFactory是DefaultApplicationContextFactory类型的</p><pre><code class=“java”>// SpringApplication中对applicationContextFactory的定义private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;// ApplicationContextFactory 中对DEFAULT的定义ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory();</code></pre><h5>ApplicationContextFactory</h5><p>咱们先来看看ApplicationContextFactory是做啥的</p><pre><code class=“java”>// 是提供给SpringApplication用来创立ConfigurableApplicationContext的策略接口// 创立上下文时不应该扭转其初始的内容,而是交给SpringApplication负责进行配置和刷新@FunctionalInterfacepublic interface ApplicationContextFactory { // 定义了ApplicationContextFactory的默认实现DEFAULT ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory(); /* * 依据传入的利用类型WebApplicationType返回其冀望的Environment的类型 * 该办法次要用在转换已有的Environment * @param webApplicationType 利用类型 * @since 2.6.14 / default Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) { return null; } /* * 依据利用类型WebApplicationType创立Environment,这个Environment随后会被设置到应用程序上下文中 * 请留神,这个办法的返回值必须和getEnvironmentType办法的保持一致 * @param webApplicationType 利用类型 * @since 2.6.14 / default ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) { return null; } /* * 创立应用程序上下文 * @param webApplicationType 利用类型 / ConfigurableApplicationContext create(WebApplicationType webApplicationType); /* * 传入一个应用程序上下文类型,创立一个会应用该类型创立上下文的ApplicationContextFactory * @param contextClass 应用程序上下文类型 * @return the factory that will instantiate the context class * @see BeanUtils#instantiateClass(Class) / static ApplicationContextFactory ofContextClass(Class<? extends ConfigurableApplicationContext> contextClass) { return of(() -> BeanUtils.instantiateClass(contextClass)); } //工具办法,与ofContextClass配套应用 static ApplicationContextFactory of(Supplier<ConfigurableApplicationContext> supplier) { return (webApplicationType) -> supplier.get(); }}</code></pre><p>总的来说,ApplicationContextFactory正如其名,次要负责创立应用程序上下文,附带会创立所需的Environment</p><h5>DefaultApplicationContextFactory</h5><p>因而,接下来咱们看一下默认的DefaultApplicationContextFactory是如何创立Environment的</p><pre><code class=“java”>class DefaultApplicationContextFactory implements ApplicationContextFactory { // 省略无关代码 @Override public Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) { return getFromSpringFactories(webApplicationType, ApplicationContextFactory::getEnvironmentType, null); } @Override public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) { return getFromSpringFactories(webApplicationType, ApplicationContextFactory::createEnvironment, null); } @Override public ConfigurableApplicationContext create(WebApplicationType webApplicationType) { try { return getFromSpringFactories(webApplicationType, ApplicationContextFactory::create, AnnotationConfigApplicationContext::new); } catch (Exception ex) { throw new IllegalStateException(“Unable create a default ApplicationContext instance, " + “you may need a custom ApplicationContextFactory”, ex); } } /* * 从spring.factories中获取ApplicationContextFactory类型的实例,遍历这些实例创立Environment * @param webApplicationType 利用类型,即后面提到的NONE,SERLVET,REACTIVE * @param action 函数,定义对每个ApplicationContextFactory调用哪个办法进行解决 * @param defaultResult 定义默认值 / private <T> T getFromSpringFactories(WebApplicationType webApplicationType, BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) { for (ApplicationContextFactory candidate : SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class, getClass().getClassLoader())) { T result = action.apply(candidate, webApplicationType); if (result != null) { return result; } } return (defaultResult != null) ? defaultResult.get() : null; }}</code></pre><p>DefaultApplicationContextFactory的设计采纳了组合模式,它自身没有太多逻辑,它的职责是通过SpringFactoriesLoader加载spring.factories中定义的ApplicationContextFactory,而后将相干逻辑交给这些spring.factories中的ApplicationContextFactory进行解决</p><h5>其余ApplicationContextFactory</h5><p>这里又看到了相熟的SpringFactoriesLoader,因而咱们持续看看默认状况下SpringBoot提供了哪些ApplicationContextFactory<br/><br/><br/><br/>简略看看SpringBoot默认提供的这些ApplicationContextFactory</p><pre><code class=“java”>// 对应响应式应用程序public class AnnotationConfigReactiveWebServerApplicationContext extends ReactiveWebServerApplicationContext implements AnnotationConfigRegistry { // 省略无关代码 static class Factory implements ApplicationContextFactory { @Override public Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) { return (webApplicationType != WebApplicationType.REACTIVE) ? null : ApplicationReactiveWebEnvironment.class; } @Override public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) { return (webApplicationType != WebApplicationType.REACTIVE) ? null : new ApplicationReactiveWebEnvironment(); } @Override public ConfigurableApplicationContext create(WebApplicationType webApplicationType) { return (webApplicationType != WebApplicationType.REACTIVE) ? null : new AnnotationConfigReactiveWebServerApplicationContext(); } }}// 对应一般Web应用程序public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { // 省略无关代码 static class Factory implements ApplicationContextFactory { @Override public Class<? extends ConfigurableEnvironment> getEnvironmentType(WebApplicationType webApplicationType) { return (webApplicationType != WebApplicationType.SERVLET) ? null : ApplicationServletEnvironment.class; } @Override public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) { return (webApplicationType != WebApplicationType.SERVLET) ? null : new ApplicationServletEnvironment(); } @Override public ConfigurableApplicationContext create(WebApplicationType webApplicationType) { return (webApplicationType != WebApplicationType.SERVLET) ? null : new AnnotationConfigServletWebServerApplicationContext(); } }}</code></pre><p>这里两个ApplicationContextFactory就别离对应SERVLET和REACTIVE两种利用类型。<br/>咱们常常应用SpringBoot开发Web应用程序,基本上都是SERVLET类型的,所以,在创立Environment时就是ApplicationServletEnvironment。本文的重点不是Environment,因而不做深刻的解析了</p><h4>对Environment进行配置,configureEnvironment</h4><pre><code class=“java”>/* * 模板办法,次要逻辑委托给configurePropertySources和configureProfiles进行解决 * 能够覆写本办法实现对environment的齐全管制 * 或者覆写configurePropertySources和configureProfiles实现部分的管制 * @param environment 应用程序的Environment * @param args 传递给run办法的参数列表 /protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { // 1. 为environment设置上转换服务ConversionService if (this.addConversionService) { environment.setConversionService(new ApplicationConversionService()); } // 2. 配置属性源 configurePropertySources(environment, args); // 3. 配置profiles configureProfiles(environment, args);}</code></pre><h5>设置转换服务ConversionService</h5><p>这里咱们简略看一下ConversionService是做啥的</p><pre><code class=“java”>/* * 用于类型转换的服务接口,ConversionService是Spring类型转换零碎的入口 * 调用convert(Object, class)能够进行一个线程平安的转换 * @since 3.0 /public interface ConversionService { /* * 判断类型sourceType是否能够转换为targetType类型 * 如果返回true,阐明能够通过convert(Object, Class)进行转换 * * 对于汇合,数组,map这些容器类型须要特地留神: * 对于这几个类型之间的互相转换,这个办法将总是返回true,即便其元素类型的转换会产生异样,也会返回true * 这就须要调用者自行处理ConversionException异样了 * @param sourceType 源类型,如果source自身就是null,则传入null * @param targetType 指标类型 * @throws 如果targetType为空,则会抛出非法参数异样 / boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType); // 和前一个函数性能一样,只是参数不一样了 boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType); /* * 将指定的source转换为指标类型target * @param source 数据源,可为null * @param targetType 指标类型,不可为null * @return 转换后果,targetType的实例 * @throws ConversionException 转换异样 * @throws IllegalArgumentException 如果targetType为null,抛出该异样 / @Nullable <T> T convert(@Nullable Object source, Class<T> targetType); // 和前一个函数性能一样,只是参数不一样了 @Nullable Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);}</code></pre><p>简略来说,ConversionService负责转换类型,将某个source转成指标类型。因为Environment接口实现了PropertyResolver,其中有一个办法为</p><pre><code class=“java”>// 获取key的属性值,并依照targetType进行返回@Nullable<T> T getProperty(String key, Class<T> targetType);</code></pre><p>因而,Environment借助了ConversionService来实现将属性值转换成targetType的性能</p><h5>配置属性源 configurePropertySources</h5><pre><code class=“java”>/* * 增加,删除或者重排序environment中的PropertySource的程序 * @param environment 应用程序的Environment实例 * @param args 传递给run办法的参数 /protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (!CollectionUtils.isEmpty(this.defaultProperties)) { DefaultPropertiesPropertySource.addOrMerge(this.defaultProperties, sources); } // addConmandLineProperties: 是否要将命令行参数作为属性源增加到Environment中,默认为true if (this.addCommandLineProperties && args.length > 0) { // 将命令行参数作为属性源增加到Environment中 String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; if (sources.contains(name)) { // 如果Environment中曾经蕴含了同名的属性源,则将这两个属性源合并后替换到原来的 PropertySource<?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource( new SimpleCommandLinePropertySource(“springApplicationCommandLineArgs”, args)); composite.addPropertySource(source); sources.replace(name, composite); } else { // 将命令行参数放到第一优先级 sources.addFirst(new SimpleCommandLinePropertySource(args)); } }}</code></pre><h5>配置profiles configureProfiles</h5><pre><code class=“java”>/* * 能够在这里配置激活哪个profile,这和spring.profiles.active并不抵触。默认不激活任何profile * @param environment Environment实例 * @param args 传递给run办法的参数 */protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {}</code></pre><h4>触发监听SpringApplicationRunListener的environmentPrepared事件</h4><p>这里对environmentPrepared事件须要非凡介绍的起因是这外面还包含一个扩大点,咱们来剖析一下<br/>后面剖析中提到,ApplicationListener也是SpringBoot提供的一个非凡扩大点,他是由默认的EventPublishingRunListener(SpringApplicationRunListener的实现)并联合事件机制实现的。而SpringBoot默认提供的ApplicationListener中有一个EnvironmentPostProcessorApplicationListener,咱们来剖析这个EnvironmentPostProcessorApplicationListener<br/><br/></p><h5>EnvironmentPostProcessorApplicationListener</h5><pre><code class=“java”>public class EnvironmentPostProcessorApplicationListener implements SmartApplicationListener, Ordered { public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10; private final DeferredLogs deferredLogs; private int order = DEFAULT_ORDER; private final Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory; // 能够回到后面的章节,在SpringApplication构造函数中创立ApplicationListener时,调用的是无参构造方法 public EnvironmentPostProcessorApplicationListener() { this(EnvironmentPostProcessorsFactory::fromSpringFactories, new DeferredLogs()); } public EnvironmentPostProcessorApplicationListener(EnvironmentPostProcessorsFactory postProcessorsFactory) { this((classloader) -> postProcessorsFactory, new DeferredLogs()); } EnvironmentPostProcessorApplicationListener( Function<ClassLoader, EnvironmentPostProcessorsFactory> postProcessorsFactory, DeferredLogs deferredLogs) { this.postProcessorsFactory = postProcessorsFactory; this.deferredLogs = deferredLogs; } // 返回监听的事件类型,次要有三类事件 // 1. ApplicationEnvironmentPreparedEvent // 2. ApplicationPreparedEvent // 3. ApplicationFailedEvent @Override public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) { return ApplicationEnvironmentPreparedEvent.class.isAssignableFrom(eventType) || ApplicationPreparedEvent.class.isAssignableFrom(eventType) || ApplicationFailedEvent.class.isAssignableFrom(eventType); } // 监听入口,能够看到最初会调用onApplicationEnvironmentPreparedEvent进行解决 @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event); } if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent(); } if (event instanceof ApplicationFailedEvent) { onApplicationFailedEvent(); } } // 最初是遍历成员属性postProcessorsFactory获取到EnvironmentPostProcessor private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); SpringApplication application = event.getSpringApplication(); for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(), event.getBootstrapContext())) { postProcessor.postProcessEnvironment(environment, application); } } private void onApplicationPreparedEvent() { finish(); } private void onApplicationFailedEvent() { finish(); } private void finish() { this.deferredLogs.switchOverAll(); } List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader, ConfigurableBootstrapContext bootstrapContext) { ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null; EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader); return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext); } @Override public int getOrder() { return this.order; } public void setOrder(int order) { this.order = order; }}</code></pre><p>这里能够看到EnvironmentPostProcessorApplicationListener的事件处理逻辑是调用外部的EnvironmentPostProcessor进行解决,而EnvironmentPostProcessor则是通过成员属性postProcessorsFactory获取到的。默认状况下,SpringBoot会调用EnvironmentPostProcessorApplicationListener的无参构造方法创立对应的监听请,而其无参构造方法中则是通过EnvironmentPostProcessorsFactory静态方法fromSpringFactories获取,对应源代码为:</p><pre><code class=“java”>static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) { return new ReflectionEnvironmentPostProcessorsFactory(classLoader, SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));}</code></pre><p>该代码会返回一个ReflectionEnvironmentPostProcessorsFactory,同时这里也看到了相熟的SpringFactoriesLoader<br/>咱们挨个来剖析</p><h5>ReflectionEnvironmentPostProcessorsFactory</h5><pre><code class=“java”>class ReflectionEnvironmentPostProcessorsFactory implements EnvironmentPostProcessorsFactory { private final List<Class<?>> classes; private ClassLoader classLoader; private final List<String> classNames; ReflectionEnvironmentPostProcessorsFactory(Class<?>… classes) { this.classes = new ArrayList<>(Arrays.asList(classes)); this.classNames = null; } ReflectionEnvironmentPostProcessorsFactory(ClassLoader classLoader, String… classNames) { this(classLoader, Arrays.asList(classNames)); } // EnvironmentPostProcessorsFactory.fromSpringFactories调用的构造函数 ReflectionEnvironmentPostProcessorsFactory(ClassLoader classLoader, List<String> classNames) { this.classes = null; this.classLoader = classLoader; this.classNames = classNames; } // 外围代码,EnvironmentPostProcessorApplicationListener 会调用这个办法来获取到 // EnvironmentPostProcessor @Override public List<EnvironmentPostProcessor> getEnvironmentPostProcessors(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext) { Instantiator<EnvironmentPostProcessor> instantiator = new Instantiator<>(EnvironmentPostProcessor.class, (parameters) -> { parameters.add(DeferredLogFactory.class, logFactory); parameters.add(Log.class, logFactory::getLog); parameters.add(ConfigurableBootstrapContext.class, bootstrapContext); parameters.add(BootstrapContext.class, bootstrapContext); parameters.add(BootstrapRegistry.class, bootstrapContext); }); return (this.classes != null) ? instantiator.instantiateTypes(this.classes) : instantiator.instantiate(this.classLoader, this.classNames); }}</code></pre><p>能够看到,ReflectionEnvironmentPostProcessorsFactory的性能就是接管spring.factories中指定的EnvironmentPostProcessor类型,并实例化后交给EnvironmentPostProcessorApplicationListener来触发</p><h5>关系图</h5><p>上面是整顿的关系图,能够帮忙理清相干关系<br/></p><h4>扩大点</h4><p></p><h3>创立应用程序上下文 createApplicationContext</h3><pre><code class=“java”>protected ConfigurableApplicationContext createApplicationContext() { return this.applicationContextFactory.create(this.webApplicationType);}</code></pre><p>这里就是调用ApplicationContextFactory进行创立Context<br/>依据前文的源码剖析咱们能够晓得,对于咱们罕用的Web应用程序来说,其ApplicationContextFactory是AnnotationConfigServletWebServerApplicationContext.Factory。其相干源码在前文能够查阅</p><h3>筹备应用程序上下文 preparedContext</h3><pre><code class=“java”>private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 设置Environment context.setEnvironment(environment); // 1. 对context进行后置解决 postProcessApplicationContext(context); // 2. 利用初始化器 applyInitializers(context); // 触发ContextPreparedEvent事件 listeners.contextPrepared(context); // 在疏导上下文中触发BootstrapContextClosedEvent事件 bootstrapContext.close(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 增加SpringBoot特有的一些单例bean到应用程序上下文中 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton(“springApplicationArguments”, applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton(“springBootBanner”, printedBanner); } if (beanFactory instanceof AbstractAutowireCapableBeanFactory) { ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences); if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } } // 增加懒加载相干的BeanFactoryPostProcessor if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context)); // 3. 对source进行解决,在这里咱们能够提前注册一些bean到应用程序上下文中 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, “Sources must not be empty”); load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context);}</code></pre><h4>对context进行后置解决 postProcessApplicationContext</h4><pre><code class=“java”>protected void postProcessApplicationContext(ConfigurableApplicationContext context) { // 注册单例的beanNameGenerator,它的性能是生成bean的名称 if (this.beanNameGenerator != null) { context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } // 设置资源加载器 if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader()); } } // 设置转换服务 if (this.addConversionService) { context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService()); }}</code></pre><p>这个后置解决就是减少一些bean,设置一些字段到context中</p><h4>利用初始化器 applyInitializers</h4><pre><code class=“java”>protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, “Unable to call initializer.”); initializer.initialize(context); }}</code></pre><p>能够看到,这里是获取了SpringApplication外部的初始化器,对context进行初始化操作。而这里的initializers是在SpringApplication构造函数中实现加载的,能够回到后面看一下,它也是从spring.factories中获取的</p><h4>对source进行解决</h4><p>source是SpringBoot提供的一种注册bean的形式,souce能够是一个class,能够是一个包Package,能够是一个XML文件或者Groovy脚本的Resource,能够是上述三种的字符串形容<br/>SpringApplication中用成员属性sources来保留这些资源</p><pre><code class=“java”>protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug(“Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } // 结构一个BeanDefinitionLoader,用于从sources中加载BeanDefinition,将其注册到context中去 BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } // 进行加载 loader.load();}</code></pre><p>这里就不进行深刻的源码解析了,总结一下source状况</p><ul><li>如果source是一个Class类型,或者其是一个Class的全限定名字符串,则会将其对应的类注册到context中,非凡状况是Groovy脚本(这种状况没见到过,然而BeanDefinitionLoader的确会进行非凡解决)</li><li>如果source是一个Package类型,或者其是一个包名的字符串,则会进行扫描,相似于ComponentScan</li><li><p>如果source是一个Resource类型,或者是一个资源的字符串表白,则会尝试将其作为XML配置文件,或者Groovy脚本文件进行加载注册</p><h4>扩大点</h4><p>某种意义上,source也是一个扩大点,但思考到很少用,暂不退出<br/><br/></p><h3>刷新应用程序上下文 refreshContext</h3><pre><code class=“java”>private void refreshContext(ConfigurableApplicationContext context) { if (this.registerShutdownHook) { shutdownHook.registerApplicationContext(context); } refresh(context);}protected void refresh(ConfigurableApplicationContext applicationContext) { applicationContext.refresh();}</code></pre><p>这里的代码比较简单,仅仅是调用applicationContext的刷新函数refresh即可。其外部是AbastactApplicationContext的刷新流程,本文暂不涉略其中</p><h3>调用SpringRunner callRunners</h3><pre><code class=“java”>private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<>(); // 从应用程序上下文中获取所有ApplicationRunner的实例 runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); // 从应用程序上下文中获取所有CommandLineRunner的实例 runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); // 依照Ordered接口或者Order注解排序 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); } }}private void callRunner(ApplicationRunner runner, ApplicationArguments args) { try { (runner).run(args); } catch (Exception ex) { throw new IllegalStateException(“Failed to execute ApplicationRunner”, ex); }}private void callRunner(CommandLineRunner runner, ApplicationArguments args) { try { (runner).run(args.getSourceArgs()); } catch (Exception ex) { throw new IllegalStateException(“Failed to execute CommandLineRunner”, ex); }}</code></pre><p>callRunners的办法比较简单,就是从容器中获取到ApplicationRunner和CommandLineRunner,调用其run办法<br/>这也算一个扩大点</p><h4>扩大点</h4><p></p><h2>总结</h2><p>SpringBoot的启动是一个非常复杂的流程,本文仅仅对SpringBoot的启动做了一些简要的梳理,同时总结了一些比拟常见的SpringBoot的扩大点</p></li></ul><p>本文由mdnice多平台公布</p></article>
...