引言:

    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.*/packageorg.springframework.core.io.support;importjava.io.IOException;importjava.net.URL;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.Collections;importjava.util.Enumeration;importjava.util.List;importjava.util.Map;importjava.util.Properties;importorg.apache.commons.logging.Log;importorg.apache.commons.logging.LogFactory;importorg.springframework.core.annotation.AnnotationAwareOrderComparator;importorg.springframework.core.io.UrlResource;importorg.springframework.lang.Nullable;importorg.springframework.util.Assert;importorg.springframework.util.ClassUtils;importorg.springframework.util.ConcurrentReferenceHashMap;importorg.springframework.util.LinkedMultiValueMap;importorg.springframework.util.MultiValueMap;importorg.springframework.util.ReflectionUtils;importorg.springframework.util.StringUtils;/*** General purpose factory loading mechanism for internal use within the framework.** <p>{@codeSpringFactoriesLoader} {@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 {@codespring.factories}* file must be in {@linkProperties} 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 {@codeexample.MyService} is the name of the interface, and {@codeMyServiceImpl1}* and {@codeMyServiceImpl2} are two implementations.**@authorArjen Poutsma*@authorJuergen Hoeller*@authorSam Brannen*@since3.2*/
public abstract classSpringFactoriesLoader {/*** 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 {@linkAnnotationAwareOrderComparator}.* <p>If a custom instantiation strategy is required, use {@link#loadFactoryNames}* to obtain all registered factory names.*@paramfactoryClass the interface or abstract class representing the factory*@paramclassLoader the ClassLoader to use for loading (can be {@codenull} to use the default)*@see#loadFactoryNames*@throwsIllegalArgumentException 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);returnresult;}/*** Load the fully qualified class names of factory implementations of the* given type from {@value#FACTORIES_RESOURCE_LOCATION}, using the given* class loader.*@paramfactoryClass the interface or abstract class representing the factory*@paramclassLoader the ClassLoader to use for loading resources; can be* {@codenull} to use the default*@see#loadFactories*@throwsIllegalArgumentException if an error occurs while loading factory names*/public static List<String> loadFactoryNames(Class<?>factoryClass, @Nullable ClassLoader classLoader) {String factoryClassName=factoryClass.getName();returnloadSpringFactories(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) {returnresult;}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= newUrlResource(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);returnresult;}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 newIllegalArgumentException("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.*/packageorg.springframework.context;/*** Callback interface for initializing a Spring {@linkConfigurableApplicationContext}* prior to being {@linkplainConfigurableApplicationContext#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 {@linkplainConfigurableApplicationContext#getEnvironment()* context's environment}. See {@codeContextLoader} and {@codeFrameworkServlet} support* for declaring a "contextInitializerClasses" context-param and init-param, respectively.** <p>{@codeApplicationContextInitializer} processors are encouraged to detect* whether Spring's {@linkorg.springframework.core.Ordered Ordered} interface has been* implemented or if the @{@linkorg.springframework.core.annotation.Order Order}* annotation is present and to sort instances accordingly if so prior to invocation.**@authorChris Beams*@since3.1*@seeorg.springframework.web.context.ContextLoader#customizeContext*@seeorg.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM*@seeorg.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses*@seeorg.springframework.web.servlet.FrameworkServlet#applyInitializers*/
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext>{/*** Initialize the given application context.*@paramapplicationContext the application to configure*/voidinitialize(C applicationContext);}

View Code

该接口在doc文档上描述很清楚了,在调用ConfigurableApplicationContext的refresh()之前进行的初始化操作

SpringApplication run方法

下面开始介绍run方法,先看一下源代码

/*** Run the Spring application, creating and refreshing a new* {@linkApplicationContext}.*@paramargs the application arguments (usually passed from a Java main method)*@returna running {@linkApplicationContext}*/publicConfigurableApplicationContext run(String... args) {StopWatch stopWatch= newStopWatch();stopWatch.start();ConfigurableApplicationContext context= null;Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();configureHeadlessProperty();SpringApplicationRunListeners listeners=getRunListeners(args);listeners.starting();try{ApplicationArguments applicationArguments= newDefaultApplicationArguments(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 newIllegalStateException(ex);}try{listeners.running(context);}catch(Throwable ex) {handleRunFailure(context, ex, exceptionReporters,null);throw newIllegalStateException(ex);}returncontext;}

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.*/packageorg.springframework.boot;importorg.springframework.context.ApplicationContext;importorg.springframework.context.ConfigurableApplicationContext;importorg.springframework.core.env.ConfigurableEnvironment;importorg.springframework.core.io.support.SpringFactoriesLoader;/*** Listener for the {@linkSpringApplication} {@coderun} method.* {@linkSpringApplicationRunListener}s are loaded via the {@linkSpringFactoriesLoader}* and should declare a public constructor that accepts a {@linkSpringApplication}* instance and a {@codeString[]} of arguments. A new* {@linkSpringApplicationRunListener} instance will be created for each run.**@authorPhillip Webb*@authorDave Syer*@authorAndy Wilkinson*/
public interfaceSpringApplicationRunListener {/*** Called immediately when the run method has first started. Can be used for very* early initialization.*/voidstarting();/*** Called once the environment has been prepared, but before the* {@linkApplicationContext} has been created.*@paramenvironment the environment*/voidenvironmentPrepared(ConfigurableEnvironment environment);/*** Called once the {@linkApplicationContext} has been created and prepared, but* before sources have been loaded.*@paramcontext the application context*/voidcontextPrepared(ConfigurableApplicationContext context);/*** Called once the application context has been loaded but before it has been* refreshed.*@paramcontext the application context*/voidcontextLoaded(ConfigurableApplicationContext context);/*** The context has been refreshed and the application has started but* {@linkCommandLineRunner CommandLineRunners} and {@linkApplicationRunner* ApplicationRunners} have not been called.*@paramcontext the application context.*@since2.0.0*/voidstarted(ConfigurableApplicationContext context);/*** Called immediately before the run method finishes, when the application context has* been refreshed and all {@linkCommandLineRunner CommandLineRunners} and* {@linkApplicationRunner ApplicationRunners} have been called.*@paramcontext the application context.*@since2.0.0*/voidrunning(ConfigurableApplicationContext context);/*** Called when a failure occurs when running the application.*@paramcontext the application context or {@codenull} if a failure occurred before* the context was created*@paramexception the failure*@since2.0.0*/voidfailed(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.*/packageorg.springframework.boot.context.event;importorg.apache.commons.logging.Log;importorg.apache.commons.logging.LogFactory;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.SpringApplicationRunListener;importorg.springframework.context.ApplicationContextAware;importorg.springframework.context.ApplicationListener;importorg.springframework.context.ConfigurableApplicationContext;importorg.springframework.context.event.ApplicationEventMulticaster;importorg.springframework.context.event.SimpleApplicationEventMulticaster;importorg.springframework.context.support.AbstractApplicationContext;importorg.springframework.core.Ordered;importorg.springframework.core.env.ConfigurableEnvironment;importorg.springframework.util.ErrorHandler;/*** {@linkSpringApplicationRunListener} to publish {@linkSpringApplicationEvent}s.* <p>* Uses an internal {@linkApplicationEventMulticaster} for the events that are fired* before the context is actually refreshed.**@authorPhillip Webb*@authorStephane Nicoll*@authorAndy Wilkinson*/
public class EventPublishingRunListener implementsSpringApplicationRunListener, Ordered {private finalSpringApplication application;private finalString[] args;private finalSimpleApplicationEventMulticaster initialMulticaster;publicEventPublishingRunListener(SpringApplication application, String[] args) {this.application =application;this.args =args;this.initialMulticaster = newSimpleApplicationEventMulticaster();for (ApplicationListener<?>listener : application.getListeners()) {this.initialMulticaster.addApplicationListener(listener);}}@Overridepublic intgetOrder() {return 0;}@Overridepublic voidstarting() {this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));}@Overridepublic voidenvironmentPrepared(ConfigurableEnvironment environment) {this.initialMulticaster.multicastEvent(newApplicationEnvironmentPreparedEvent(this.application, this.args, environment));}@Overridepublic voidcontextPrepared(ConfigurableApplicationContext context) {}@Overridepublic voidcontextLoaded(ConfigurableApplicationContext context) {for (ApplicationListener<?> listener : this.application.getListeners()) {if (listener instanceofApplicationContextAware) {((ApplicationContextAware) listener).setApplicationContext(context);}context.addApplicationListener(listener);}this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));}@Overridepublic voidstarted(ConfigurableApplicationContext context) {context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));}@Overridepublic voidrunning(ConfigurableApplicationContext context) {context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));}@Overridepublic voidfailed(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 instanceofAbstractApplicationContext) {for (ApplicationListener<?>listener : ((AbstractApplicationContext) context).getApplicationListeners()) {this.initialMulticaster.addApplicationListener(listener);}}this.initialMulticaster.setErrorHandler(newLoggingErrorHandler());this.initialMulticaster.multicastEvent(event);}}private static class LoggingErrorHandler implementsErrorHandler {private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);@Overridepublic voidhandleError(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.*/packageorg.springframework.context.event;importjava.util.concurrent.Executor;importorg.apache.commons.logging.Log;importorg.apache.commons.logging.LogFactory;importorg.springframework.beans.factory.BeanFactory;importorg.springframework.context.ApplicationEvent;importorg.springframework.context.ApplicationListener;importorg.springframework.core.ResolvableType;importorg.springframework.lang.Nullable;importorg.springframework.util.ErrorHandler;/*** Simple implementation of the {@linkApplicationEventMulticaster} 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 {@codeinstanceof}* 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.**@authorRod Johnson*@authorJuergen Hoeller*@authorStephane Nicoll*@see#setTaskExecutor*/
public class SimpleApplicationEventMulticaster extendsAbstractApplicationEventMulticaster {@NullableprivateExecutor taskExecutor;@NullableprivateErrorHandler errorHandler;/*** Create a new SimpleApplicationEventMulticaster.*/publicSimpleApplicationEventMulticaster() {}/*** Create a new SimpleApplicationEventMulticaster for the given BeanFactory.*/publicSimpleApplicationEventMulticaster(BeanFactory beanFactory) {setBeanFactory(beanFactory);}/*** Set a custom executor (typically a {@linkorg.springframework.core.task.TaskExecutor})* to invoke each listener with.* <p>Default is equivalent to {@linkorg.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.*@seeorg.springframework.core.task.SyncTaskExecutor*@seeorg.springframework.core.task.SimpleAsyncTaskExecutor*/public voidsetTaskExecutor(@Nullable Executor taskExecutor) {this.taskExecutor =taskExecutor;}/*** Return the current task executor for this multicaster.*/@NullableprotectedExecutor getTaskExecutor() {return this.taskExecutor;}/*** Set the {@linkErrorHandler} 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 {@linkErrorHandler} implementation that catches* and logs exceptions (a la* {@linkorg.springframework.scheduling.support.TaskUtils#LOG_AND_SUPPRESS_ERROR_HANDLER})* or an implementation that logs exceptions while nevertheless propagating them* (e.g. {@linkorg.springframework.scheduling.support.TaskUtils#LOG_AND_PROPAGATE_ERROR_HANDLER}).*@since4.1*/public voidsetErrorHandler(@Nullable ErrorHandler errorHandler) {this.errorHandler =errorHandler;}/*** Return the current error handler for this multicaster.*@since4.1*/@NullableprotectedErrorHandler getErrorHandler() {return this.errorHandler;}@Overridepublic voidmulticastEvent(ApplicationEvent event) {multicastEvent(event, resolveDefaultEventType(event));}@Overridepublic void multicastEvent(finalApplicationEvent 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);}}}privateResolvableType resolveDefaultEventType(ApplicationEvent event) {returnResolvableType.forInstance(event);}/*** Invoke the given listener with the given event.*@paramlistener the ApplicationListener to invoke*@paramevent the current event to propagate*@since4.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 voiddoInvokeListener(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{throwex;}}}private booleanmatchesClassCastMessage(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事件

    privateConfigurableEnvironment 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= newEnvironmentConverter(getClassLoader()).convertToStandardEnvironmentIfNecessary(environment);}ConfigurationPropertySources.attach(environment);returnenvironment;}

View Code

5.根据当前environment打印Banner

也就是启动时控制台打印出的Spring标志 启动的Banner就是在这一步打印出来的  也可自定义Banner 链接:Spring Boot实例教程 - 自定义Banner

6.创建的ApplicationContext对象

这里会根据this.webEnvironment的属性值来确定创建的ApplicationContext对象

    /*** Strategy method used to create the {@linkApplicationContext}. By default this* method will respect any explicitly set application context or application context* class before falling back to a suitable default.*@returnthe application context (not yet refreshed)*@see#setApplicationContextClass(Class)*/protectedConfigurableApplicationContext createApplicationContext() {Class<?> contextClass = this.applicationContextClass;if (contextClass == null) {try{switch (this.webApplicationType) {caseSERVLET:contextClass=Class.forName(DEFAULT_WEB_CONTEXT_CLASS);break;caseREACTIVE:contextClass=Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);break;default:contextClass=Class.forName(DEFAULT_CONTEXT_CLASS);}}catch(ClassNotFoundException ex) {throw newIllegalStateException("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 voidprepareContext(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() throwsBeansException, 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.throwex;}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 voidhandleRunFailure(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启动机制相关推荐

  1. SpringBoot学习笔记(4)----SpringBoot中freemarker、thymeleaf的使用

    1. freemarker引擎的使用 如果你使用的是idea或者eclipse中安装了sts插件,那么在新建项目时就可以直接指定试图模板 如图: 勾选freeMarker,此时springboot项目 ...

  2. SpringBoot学习笔记(9)----SpringBoot中使用关系型数据库以及事务处理

    在实际的运用开发中,跟数据库之间的交互是必不可少的,SpringBoot也提供了两种跟数据库交互的方式. 1. 使用JdbcTemplate 在SpringBoot中提供了JdbcTemplate模板 ...

  3. 【Springboot学习笔记】SpringBoot+Mybatis+Thymeleaf+Layui数据表单从零开始实现按条件模糊分页查询的方法

    [Springboot学习笔记]SpringBoot+Mybatis+Thymeleaf+Layui数据表单从零开始实现按条件模糊分页查询的方法 目录 1.搭建环境 1.1直接从网上下载SpringB ...

  4. SpringBoot学习笔记(16)----SpringBoot整合Swagger2

    Swagger 是一个规范和完整的框架,用于生成,描述,调用和可视化RESTful风格的web服务 http://swagger.io Springfox的前身是swagger-springmvc,是 ...

  5. SpringBoot 学习二:操作数据库

    2019独角兽企业重金招聘Python工程师标准>>> 本文将从以下几个方面介绍: 前言 配置数据源 SpringBoot 整合 Mybatis SpringBoot 整合 Jdbc ...

  6. springBoot学习(二) 基础运行原理

    结论:SpringBoot所有的自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应 ...

  7. 2.SpringBoot学习(二)——Spring Boot ConfigurationProperties

    1.简介 1.1 概述 Annotation for externalized configuration. Add this to a class definition or a @Bean met ...

  8. SpringBoot(十二):springboot如何测试打包部署

    原文出处: 纯洁的微笑 有很多网友会时不时的问我,spring boot项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下spring boot 如何开发.调试.打包到最后的投 ...

  9. springboot学习笔记-5 springboot整合shiro

    shiro是一个权限框架,具体的使用可以查看其官网 http://shiro.apache.org/  它提供了很方便的权限认证和登录的功能. 而springboot作为一个开源框架,必然提供了和sh ...

  10. springboot学习(七十三) springboot中使用springdoc替换swagger(springfox)

    文章目录 前言 一.springdoc介绍 二.使用步骤 1.引入库 2. 创建一个spring配置类,添加springdoc的配置 3. 常用的swagger注解和springdoc的对应关系 4. ...

最新文章

  1. java正则表达式课程_通过此免费课程学习正则表达式
  2. 皮一皮:你有没有为中国大数据力量做一份贡献!
  3. 软件测试类工具大全2009版(一)
  4. Spring面试问题与解答
  5. 如何成为Linux平台C语言程序员(转)
  6. 【李宏毅2020 ML/DL】P45-50 Network Compression
  7. sql-插入当前时间
  8. Ubuntu 18.04 安装无线网卡
  9. 青果教务php,拟物校园 | 拟物校园,一个高校教务系统爬虫,现支持正方教务、青果教务。...
  10. 【金融市场基础知识】——中国的金融体系(二)
  11. java-word模板导出
  12. 站内文章被百度收录的方法
  13. Pytorch(三):数据变换 Transforms
  14. python外国网站爬虫_用python爬过这些网站,才敢说自己会爬虫!
  15. linux floppy 虚拟,Floppylinux
  16. win10右键 发送到 选项消失解决办法
  17. 《卡耐基三部曲》(Yanlz+VR云游戏+Unity+SteamVR+云技术+5G+AI+人性的弱点+人性的优点+语言的突破+术业有专攻+世界观+人生观+价值观+志同道合+不卑不亢+立钻哥哥++==)
  18. 百度编辑器ueditor,编辑内容过多时,工具栏不可见,不方便编辑或上传问题
  19. 一年成为Emacs高手(像神一样使用编辑器)
  20. pos机骗局收取押金如何投诉-真实案列解答

热门文章

  1. 按单词逆序句子(含标点)
  2. html5 audio音频播放全解析
  3. 丢了好几年的 Auto CAD又拿起来......
  4. 闪屏,default
  5. 正则表达式的知识普及
  6. 数据库安全:不只是DAM
  7. 高性能网站建设之 MS Sql Server数据库分区
  8. JAVA面试题总汇(含答案)
  9. linux下面破解rar压缩包密码
  10. 关于网上cython书籍的调研