日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot学习(二)探究Springboot启动机制

發布時間:2023/12/20 javascript 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot学习(二)探究Springboot启动机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引言:

    SpringBoot為我們做的自動配置,確實方便快捷,但是對于新手來說,如果不大懂SpringBoot內部啟動原理,以后難免會吃虧。所以這次博主就跟你們一起探究一下SpringBoot的啟動原理。

目錄

    啟動流程圖

    啟動類

    啟動分析

    啟動總結

啟動流程圖

總覽:

? ? ? ?上圖為SpringBoot啟動結構圖,我們發現啟動流程主要分為三個部分,第一部分進行SpringApplication的初始化模塊,配置一些基本的環境變量、資源、構造器、監聽器,第二部分實現了應用具體的啟動方案,包括啟動流程的監聽模塊、加載配置環境模塊、及核心的創建上下文環境模塊,第三部分是自動化配置模塊,該模塊作為springboot自動配置核心,在后面的分析中會詳細討論。在下面的啟動程序中我們會串聯起結構中的主要功能。(摘自:SpringBoot啟動流程解析)

SpringBoot啟動類

    從上面代碼可以看出,別看它只是定義了 @SpringBootApplication 這個Annotation 和 調用SpringApplication.run方法,但是它們實現的功能可不是一星半點的。

?

SpringApplication啟動分析

    注:我這里是2.0.3版本的可能有些地方不太一樣

    參考鏈接:深入理解SpringBoot之啟動探究

?

SpringaApplication初始化分析

首先進入run方法

會new一個SpringApplication實例,進入SpringApplication構造方法,它會調用initialize()進行初始化?

首先它會去判斷運行環境根據運行環境來創建是java得ApplicationContext對象或者是web得ApplicationContext對象。

這里大家重點關注一下源代碼中getSpringFacoriesInstances方法, ApplicationListener接口,ApplicationContextInitializer接口,這些接口都是通過SpringFactoriesLoader從META-INF/spring.factories文件里加載的

看一下getSpringFacoriesInstances方法的源碼

這里有一個關鍵類叫做SpringFactoriesLoader 該類的主要作用就是去讀取META-INF/spring.factories配置文件里配置的引導對象,我們來看一下代碼:

/** Copyright 2002-2018 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.core.io.support;import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.Properties;import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.io.UrlResource; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils;/*** General purpose factory loading mechanism for internal use within the framework.** <p>{@code SpringFactoriesLoader} {@linkplain #loadFactories loads} and instantiates* factories of a given type from {@value #FACTORIES_RESOURCE_LOCATION} files which* may be present in multiple JAR files in the classpath. The {@code spring.factories}* file must be in {@link Properties} format, where the key is the fully qualified* name of the interface or abstract class, and the value is a comma-separated list of* implementation class names. For example:** <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>** where {@code example.MyService} is the name of the interface, and {@code MyServiceImpl1}* and {@code MyServiceImpl2} are two implementations.** @author Arjen Poutsma* @author Juergen Hoeller* @author Sam Brannen* @since 3.2*/ public abstract class SpringFactoriesLoader {/*** The location to look for factories.* <p>Can be present in multiple JAR files.*/public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();/*** Load and instantiate the factory implementations of the given type from* {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader.* <p>The returned factories are sorted through {@link AnnotationAwareOrderComparator}.* <p>If a custom instantiation strategy is required, use {@link #loadFactoryNames}* to obtain all registered factory names.* @param factoryClass the interface or abstract class representing the factory* @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)* @see #loadFactoryNames* @throws IllegalArgumentException if any factory implementation class cannot* be loaded or if an error occurs while instantiating any factory*/public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {Assert.notNull(factoryClass, "'factoryClass' must not be null");ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);if (logger.isTraceEnabled()) {logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);}List<T> result = new ArrayList<>(factoryNames.size());for (String factoryName : factoryNames) {result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));}AnnotationAwareOrderComparator.sort(result);return result;}/*** Load the fully qualified class names of factory implementations of the* given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given* class loader.* @param factoryClass the interface or abstract class representing the factory* @param classLoader the ClassLoader to use for loading resources; can be* {@code null} to use the default* @see #loadFactories* @throws IllegalArgumentException if an error occurs while loading factory names*/public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {String factoryClassName = factoryClass.getName();return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry<?, ?> entry : properties.entrySet()) {List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));result.addAll((String) entry.getKey(), factoryClassNames);}}cache.put(classLoader, result);return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}}@SuppressWarnings("unchecked")private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {try {Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);if (!factoryClass.isAssignableFrom(instanceClass)) {throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");}return (T) ReflectionUtils.accessibleConstructor(instanceClass).newInstance();}catch (Throwable ex) {throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), ex);}}} View Code

通過SpringFactoriesLoader找到META-INF/spring.factories下ApplicationContextInitializer的實現類并將其實例化

再看一下ApplicationListener的源碼

/** Copyright 2002-2011 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.context;/*** Callback interface for initializing a Spring {@link ConfigurableApplicationContext}* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.** <p>Typically used within web applications that require some programmatic initialization* of the application context. For example, registering property sources or activating* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support* for declaring a "contextInitializerClasses" context-param and init-param, respectively.** <p>{@code ApplicationContextInitializer} processors are encouraged to detect* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been* implemented or if the @{@link org.springframework.core.annotation.Order Order}* annotation is present and to sort instances accordingly if so prior to invocation.** @author Chris Beams* @since 3.1* @see org.springframework.web.context.ContextLoader#customizeContext* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses* @see org.springframework.web.servlet.FrameworkServlet#applyInitializers*/ public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {/*** Initialize the given application context.* @param applicationContext the application to configure*/void initialize(C applicationContext);} View Code

該接口在doc文檔上描述很清楚了,在調用ConfigurableApplicationContext的refresh()之前進行的初始化操作

SpringApplication run方法

下面開始介紹run方法,先看一下源代碼

/*** Run the Spring application, creating and refreshing a new* {@link ApplicationContext}.* @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();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;} View Code

1.啟動計時

stopWatch.start();

2.創建了應用的監聽器SpringApplicationRunListeners并開始監聽

?該接口首先從META-INF/spring.factories文件里獲取所有配置的SpringApplicationRunner ,那么這個接口時干啥的呢?

我們來看一下源代碼:

/** Copyright 2012-2018 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.boot;import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.io.support.SpringFactoriesLoader;/*** Listener for the {@link SpringApplication} {@code run} method.* {@link SpringApplicationRunListener}s are loaded via the {@link SpringFactoriesLoader}* and should declare a public constructor that accepts a {@link SpringApplication}* instance and a {@code String[]} of arguments. A new* {@link SpringApplicationRunListener} instance will be created for each run.** @author Phillip Webb* @author Dave Syer* @author Andy Wilkinson*/ public interface SpringApplicationRunListener {/*** Called immediately when the run method has first started. Can be used for very* early initialization.*/void starting();/*** Called once the environment has been prepared, but before the* {@link ApplicationContext} has been created.* @param environment the environment*/void environmentPrepared(ConfigurableEnvironment environment);/*** Called once the {@link ApplicationContext} has been created and prepared, but* before sources have been loaded.* @param context the application context*/void contextPrepared(ConfigurableApplicationContext context);/*** Called once the application context has been loaded but before it has been* refreshed.* @param context the application context*/void contextLoaded(ConfigurableApplicationContext context);/*** The context has been refreshed and the application has started but* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner* ApplicationRunners} have not been called.* @param context the application context.* @since 2.0.0*/void started(ConfigurableApplicationContext context);/*** Called immediately before the run method finishes, when the application context has* been refreshed and all {@link CommandLineRunner CommandLineRunners} and* {@link ApplicationRunner ApplicationRunners} have been called.* @param context the application context.* @since 2.0.0*/void running(ConfigurableApplicationContext context);/*** Called when a failure occurs when running the application.* @param context the application context or {@code null} if a failure occurred before* the context was created* @param exception the failure* @since 2.0.0*/void failed(ConfigurableApplicationContext context, Throwable exception);} View Code

看出其中包括:程序啟動,環境準備,ApplicationContext準備加載,程序啟動 運行 完成等。

該接口默認有一個實現類EventPublishingRunListener至關重要大家需要了解一下:

/** Copyright 2012-2018 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.boot.context.event;import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplicationRunListener; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.event.ApplicationEventMulticaster; import org.springframework.context.event.SimpleApplicationEventMulticaster; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.util.ErrorHandler;/*** {@link SpringApplicationRunListener} to publish {@link SpringApplicationEvent}s.* <p>* Uses an internal {@link ApplicationEventMulticaster} for the events that are fired* before the context is actually refreshed.** @author Phillip Webb* @author Stephane Nicoll* @author Andy Wilkinson*/ 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);}}@Overridepublic int getOrder() {return 0;}@Overridepublic void starting() {this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));}@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {}@Overridepublic 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));}@Overridepublic void started(ConfigurableApplicationContext context) {context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));}@Overridepublic void running(ConfigurableApplicationContext context) {context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));}@Overridepublic 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 of the context's listeners insteadif (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 Log logger = LogFactory.getLog(EventPublishingRunListener.class);@Overridepublic void handleError(Throwable throwable) {logger.warn("Error calling ApplicationEventListener", throwable);}}} View Code

其中SimpleApplicationEventMulticaster這個類很重要

看一下源碼

/** Copyright 2002-2018 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package org.springframework.context.event;import java.util.concurrent.Executor;import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;import org.springframework.beans.factory.BeanFactory; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.util.ErrorHandler;/*** Simple implementation of the {@link ApplicationEventMulticaster} interface.** <p>Multicasts all events to all registered listeners, leaving it up to* the listeners to ignore events that they are not interested in.* Listeners will usually perform corresponding {@code instanceof}* checks on the passed-in event object.** <p>By default, all listeners are invoked in the calling thread.* This allows the danger of a rogue listener blocking the entire application,* but adds minimal overhead. Specify an alternative task executor to have* listeners executed in different threads, for example from a thread pool.** @author Rod Johnson* @author Juergen Hoeller* @author Stephane Nicoll* @see #setTaskExecutor*/ public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {@Nullableprivate Executor taskExecutor;@Nullableprivate ErrorHandler errorHandler;/*** Create a new SimpleApplicationEventMulticaster.*/public SimpleApplicationEventMulticaster() {}/*** Create a new SimpleApplicationEventMulticaster for the given BeanFactory.*/public SimpleApplicationEventMulticaster(BeanFactory beanFactory) {setBeanFactory(beanFactory);}/*** Set a custom executor (typically a {@link org.springframework.core.task.TaskExecutor})* to invoke each listener with.* <p>Default is equivalent to {@link org.springframework.core.task.SyncTaskExecutor},* executing all listeners synchronously in the calling thread.* <p>Consider specifying an asynchronous task executor here to not block the* caller until all listeners have been executed. However, note that asynchronous* execution will not participate in the caller's thread context (class loader,* transaction association) unless the TaskExecutor explicitly supports this.* @see org.springframework.core.task.SyncTaskExecutor* @see org.springframework.core.task.SimpleAsyncTaskExecutor*/public void setTaskExecutor(@Nullable Executor taskExecutor) {this.taskExecutor = taskExecutor;}/*** Return the current task executor for this multicaster.*/@Nullableprotected Executor getTaskExecutor() {return this.taskExecutor;}/*** Set the {@link ErrorHandler} to invoke in case an exception is thrown* from a listener.* <p>Default is none, with a listener exception stopping the current* multicast and getting propagated to the publisher of the current event.* If a {@linkplain #setTaskExecutor task executor} is specified, each* individual listener exception will get propagated to the executor but* won't necessarily stop execution of other listeners.* <p>Consider setting an {@link ErrorHandler} implementation that catches* and logs exceptions (a la* {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_SUPPRESS_ERROR_HANDLER})* or an implementation that logs exceptions while nevertheless propagating them* (e.g. {@link org.springframework.scheduling.support.TaskUtils#LOG_AND_PROPAGATE_ERROR_HANDLER}).* @since 4.1*/public void setErrorHandler(@Nullable ErrorHandler errorHandler) {this.errorHandler = errorHandler;}/*** Return the current error handler for this multicaster.* @since 4.1*/@Nullableprotected ErrorHandler getErrorHandler() {return this.errorHandler;}@Overridepublic void multicastEvent(ApplicationEvent event) {multicastEvent(event, resolveDefaultEventType(event));}@Overridepublic void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {Executor executor = getTaskExecutor();if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {invokeListener(listener, event);}}}private ResolvableType resolveDefaultEventType(ApplicationEvent event) {return ResolvableType.forInstance(event);}/*** Invoke the given listener with the given event.* @param listener the ApplicationListener to invoke* @param event the current event to propagate* @since 4.1*/protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {ErrorHandler errorHandler = getErrorHandler();if (errorHandler != null) {try {doInvokeListener(listener, event);}catch (Throwable err) {errorHandler.handleError(err);}}else {doInvokeListener(listener, event);}}@SuppressWarnings({"unchecked", "rawtypes"})private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {listener.onApplicationEvent(event);}catch (ClassCastException ex) {String msg = ex.getMessage();if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {// Possibly a lambda-defined listener which we could not resolve the generic event type for// -> let's suppress the exception and just log a debug message.Log logger = LogFactory.getLog(getClass());if (logger.isDebugEnabled()) {logger.debug("Non-matching event type for listener: " + listener, ex);}}else {throw ex;}}}private boolean matchesClassCastMessage(String classCastMessage, String eventClassName) {// On Java 8, the message simply starts with the class name: "java.lang.String cannot be cast..."if (classCastMessage.startsWith(eventClassName)) {return true;}// On Java 9, the message contains the module name: "java.base/java.lang.String cannot be cast..."int moduleSeparatorIndex = classCastMessage.indexOf('/');if (moduleSeparatorIndex != -1 && classCastMessage.startsWith(eventClassName, moduleSeparatorIndex + 1)) {return true;}// Assuming an unrelated class cast failure...return false;}} View Code

doInvokeListener方法,該方法用于執行事件的監聽方法。該類最主要作用就是通知各個階段的listener處理對應階段的事件

3.調用SpringApplicationRunListener的start方法

看一下這個方法

public void starting() {for (SpringApplicationRunListener listener : this.listeners) {listener.starting();}}

4.調用prepareEnvironment事件

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {// Create and configure the environmentConfigurableEnvironment environment = getOrCreateEnvironment();configureEnvironment(environment, applicationArguments.getSourceArgs());listeners.environmentPrepared(environment);bindToSpringApplication(environment);if (this.webApplicationType == WebApplicationType.NONE) {environment = new EnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);}ConfigurationPropertySources.attach(environment);return environment;} View Code

5.根據當前environment打印Banner

也就是啟動時控制臺打印出的Spring標志 啟動的Banner就是在這一步打印出來的? 也可自定義Banner 鏈接:Spring Boot實例教程 - 自定義Banner

6.創建的ApplicationContext對象

這里會根據this.webEnvironment的屬性值來確定創建的ApplicationContext對象

/*** Strategy method used to create the {@link ApplicationContext}. By default this* method will respect any explicitly set application context or application context* class before falling back to a suitable default.* @return the application context (not yet refreshed)* @see #setApplicationContextClass(Class)*/protected ConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try {switch (this.webApplicationType) {case SERVLET:contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);break;case REACTIVE:contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default: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);} View Code

如果是web環境那就創建org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext

否則就創建org.springframework.context.annotation.AnnotationConfigApplicationContext

7.調用prepareContext方法

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 beanscontext.getBeanFactory().registerSingleton("springApplicationArguments",applicationArguments);if (printedBanner != null) {context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);}// Load the sourcesSet<Object> sources = getAllSources();Assert.notEmpty(sources, "Sources must not be empty");load(context, sources.toArray(new Object[0]));listeners.contextLoaded(context);} View Code

8.調用refreshContext

該方法最終會執行AbstractApplicationContext的refresh()方法,我在這里貼一下源代碼:

@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing. prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory);// Initialize message source for this context. initMessageSource();// Initialize event multicaster for this context. initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses. onRefresh();// Check for listener beans and register them. registerListeners();// Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event. finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources. destroyBeans();// Reset 'active' flag. cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore... resetCommonCaches();}}} View Code

在這個方法里會初始化BeanFactory 初始化BeanFactoryPostProcessor 注冊BeanPostProcessor 初始化MessageSource 注冊事件監聽器等操作。建議大家深入了解Spring的IOC加載原理

9.調用afterRefresh方法

/*** Called after the context has been refreshed.* @param context the application context* @param args the application arguments*/protected void afterRefresh(ConfigurableApplicationContext context,ApplicationArguments args) {}

10.啟動計時結束

stopWatch.stop();

11.調用started方法

public void started(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {listener.started(context);}}

12.調用callRunners方法

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);}}}

該方法會從IOC容器里找到ApplicationRunner或者CommandLineRunner并執行其run方法,當我們需要在SpringBoot程序啟動時處理我們自己的邏輯,那么就可以實現上述接口

13.啟動異常處理

private void handleRunFailure(ConfigurableApplicationContext context,Throwable exception,Collection<SpringBootExceptionReporter> exceptionReporters,SpringApplicationRunListeners listeners) {try {try {handleExitCode(context, exception);if (listeners != null) {listeners.failed(context, exception);}}finally {reportFailure(exceptionReporters, exception);if (context != null) {context.close();}}}catch (Exception ex) {logger.warn("Unable to close ApplicationContext", ex);}ReflectionUtils.rethrowRuntimeException(exception);} View Code

至此run方法就執行完成了

SpringBoot啟動總結

1. ?SpringBoot啟動時SpringApplicationRunListener接口的相關方法至關重要,它定義了啟動時的各個“時間點”。

2. SpringBoot可以從spring.factoies文件里讀取配置的ApplicationListener

3. META-INF文件夾下的spring.factoies文件是SpringBoot啟動的核心文件,SpringFatoriesLoader會讀取該文件夾下的相關配置作為引導

4. SpringBoot啟動時利用了事件機制,來發送啟動時各個周期階段的事件?

?

轉載于:https://www.cnblogs.com/yanfeiLiu/p/9303999.html

總結

以上是生活随笔為你收集整理的SpringBoot学习(二)探究Springboot启动机制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。