本文從源代碼的角度來看看Spring Boot的啓動過程究竟是怎麼樣的,爲什麼以往紛繁複雜的配置到現在能夠這麼簡便。java
@SpringBootApplication public class HelloWorldMainApplication { public static void main(String[] args) { SpringApplication.run(HelloWorldMainApplication.class, args); } }
@SpringBootApplication咱們上一篇文章中大概的講過了,有興趣的能夠看看我第一篇關於SpringBoot的文章,本篇文章主要關注SpringApplication.run(HelloWorldMainApplication.class, args);,咱們跟進去看看react
// 調用靜態類,參數對應的就是HelloWorldMainApplication.class以及main方法中的args public static ConfigurableApplicationContext run(Class<?> primarySource,String... args) { return run(new Class<?>[] { primarySource }, args); } public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return (new SpringApplication(sources)).run(args); }
它實際上會構造一個SpringApplication的實例,並把咱們的啓動類HelloWorldMainApplication.class做爲參數傳進去,而後運行它的run方法web
SpringApplication構造器spring
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //把HelloWorldMainApplication.class設置爲屬性存儲起來 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //設置應用類型是Standard仍是Web this.webApplicationType = deduceWebApplicationType(); //設置初始化器(Initializer),最後會調用這些初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //設置監聽器(Listener) setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
先將HelloWorldMainApplication.class存儲在this.primarySources屬性中設計模式
private WebApplicationType deduceWebApplicationType() { if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; } // 相關常量 private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework." + "web.reactive.DispatcherHandler"; private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework." + "web.servlet.DispatcherServlet"; private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };
這裏主要是經過類加載器判斷REACTIVE
相關的Class是否存在,若是不存在,則web環境即爲SERVLET
類型。這裏設置好web環境類型,在後面會根據類型初始化對應環境。你們還記得咱們第一篇文章中引入的依賴嗎?springboot
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
spring-boot-starter-web 的pom又會引入Tomcat和spring-webmvc,以下mvc
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.0.5.RELEASE</version> <scope>compile</scope> </dependency>
咱們來看看spring-webmvc這個jar包app
很明顯spring-webmvc中存在DispatcherServlet這個類,也就是咱們之前SpringMvc的核心Servlet,經過類加載能加載DispatcherServlet這個類,那麼咱們的應用類型天然就是WebApplicationType.SERVLETless
public enum WebApplicationType { NONE, SERVLET, REACTIVE; private WebApplicationType() { } }
//設置初始化器(Initializer),最後會調用這些初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
咱們先來看看getSpringFactoriesInstances( ApplicationContextInitializer.class)異步
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } // 這裏的入參type就是ApplicationContextInitializer.class private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 使用Set保存names來避免重複元素 Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 根據names來進行實例化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 對實例進行排序 AnnotationAwareOrderComparator.sort(instances); return instances; }
這裏面首先會根據入參type讀取全部的names(是一個String集合),而後根據這個集合來完成對應的實例化操做:
// 入參就是ApplicationContextInitializer.class public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { //從類路徑的META-INF/spring.factories中加載全部默認的自動配置類 Enumeration<URL> urls = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories"); ArrayList result = new ArrayList(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); //獲取ApplicationContextInitializer.class的全部值 String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException var8) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8); } }
這個方法會嘗試從類路徑的META-INF/spring.factories處讀取相應配置文件,而後進行遍歷,讀取配置文件中Key爲:org.springframework.context.ApplicationContextInitializer的value。以spring-boot-autoconfigure這個包爲例,它的META-INF/spring.factories部分定義以下所示:
org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
這兩個類名會被讀取出來,而後放入到Set<String>集合中,準備開始下面的實例化操做:
// parameterTypes: 上一步獲得的names集合 private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<T>(names.size()); for (String name : names) { try { Class<?> instanceClass = ClassUtils.forName(name, classLoader); //確認被加載類是ApplicationContextInitializer的子類 Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes); //反射實例化對象 T instance = (T) BeanUtils.instantiateClass(constructor, args); //加入List集合中 instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex); } } return instances; }
確認被加載的類確實是org.springframework.context.ApplicationContextInitializer的子類,而後就是獲得構造器進行初始化,最後放入到實例列表中。
所以,所謂的初始化器就是org.springframework.context.ApplicationContextInitializer的實現類,這個接口是這樣定義的:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> { void initialize(C applicationContext); }
在Spring上下文被刷新以前進行初始化的操做。典型地好比在Web應用中,註冊Property Sources或者是激活Profiles。Property Sources比較好理解,就是配置文件。Profiles是Spring爲了在不一樣環境下(如DEV,TEST,PRODUCTION等),加載不一樣的配置項而抽象出來的一個實體。
下面開始設置監聽器:
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
咱們仍是跟進代碼看看getSpringFactoriesInstances
// 這裏的入參type是:org.springframework.context.ApplicationListener.class private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<String>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
能夠發現,這個加載相應的類名,而後完成實例化的過程和上面在設置初始化器時一模一樣,一樣,仍是以spring-boot-autoconfigure這個包中的spring.factories爲例,看看相應的Key-Value:
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
這10個監聽器會貫穿springBoot整個生命週期。至此,對於SpringApplication實例的初始化過程就結束了。
完成了SpringApplication實例化,下面開始調用run方法:
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); // 第二步:根據SpringApplicationRunListeners以及參數來準備環境 ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); configureIgnoreBeanInfo(environment); // 準備Banner打印器 - 就是啓動Spring Boot的時候打印在console上的ASCII藝術字體 Banner printedBanner = printBanner(environment); // 第三步:建立Spring容器 context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // 第四步:Spring容器前置處理 prepareContext(context, environment, listeners, applicationArguments,printedBanner); // 第五步:刷新容器 refreshContext(context);
// 第六步:Spring容器後置處理 afterRefresh(context, applicationArguments); // 第七步:發出結束執行的事件 listeners.started(context); // 第八步:執行Runners this.callRunners(context, applicationArguments); stopWatch.stop(); // 返回容器 return context; } catch (Throwable ex) { handleRunFailure(context, listeners, exceptionReporters, ex); throw new IllegalStateException(ex); } }
下面具體分析。
獲取監聽器
跟進getRunListeners
方法:
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); }
這裏仍然利用了getSpringFactoriesInstances方法來獲取實例,你們能夠看看前面的這個方法分析,從META-INF/spring.factories中讀取Key爲org.springframework.boot.SpringApplicationRunListener的Values:
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
getSpringFactoriesInstances中反射獲取實例時會觸發EventPublishingRunListener
的構造函數,咱們來看看EventPublishingRunListener
的構造函數:
1 public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { 2 private final SpringApplication application; 3 private final String[] args; 4 //廣播器 5 private final SimpleApplicationEventMulticaster initialMulticaster; 6 7 public EventPublishingRunListener(SpringApplication application, String[] args) { 8 this.application = application; 9 this.args = args; 10 this.initialMulticaster = new SimpleApplicationEventMulticaster(); 11 Iterator var3 = application.getListeners().iterator(); 12 13 while(var3.hasNext()) { 14 ApplicationListener<?> listener = (ApplicationListener)var3.next(); 15 //將上面設置到SpringApplication的十一個監聽器所有添加到SimpleApplicationEventMulticaster這個廣播器中 16 this.initialMulticaster.addApplicationListener(listener); 17 } 18 19 } 20 //略... 21 }
咱們看到EventPublishingRunListener裏面有一個廣播器,EventPublishingRunListener 的構造方法將SpringApplication的十一個監聽器所有添加到SimpleApplicationEventMulticaster這個廣播器中,咱們來看看是如何添加到廣播器:
1 public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware { 2 //廣播器的父類中存放保存監聽器的內部內 3 private final AbstractApplicationEventMulticaster.ListenerRetriever defaultRetriever = new AbstractApplicationEventMulticaster.ListenerRetriever(false); 4 5 @Override 6 public void addApplicationListener(ApplicationListener<?> listener) { 7 synchronized (this.retrievalMutex) { 8 Object singletonTarget = AopProxyUtils.getSingletonTarget(listener); 9 if (singletonTarget instanceof ApplicationListener) { 10 this.defaultRetriever.applicationListeners.remove(singletonTarget); 11 } 12 //內部類對象 13 this.defaultRetriever.applicationListeners.add(listener); 14 this.retrieverCache.clear(); 15 } 16 } 17 18 private class ListenerRetriever { 19 //保存全部的監聽器 20 public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet(); 21 public final Set<String> applicationListenerBeans = new LinkedHashSet(); 22 private final boolean preFiltered; 23 24 public ListenerRetriever(boolean preFiltered) { 25 this.preFiltered = preFiltered; 26 } 27 28 public Collection<ApplicationListener<?>> getApplicationListeners() { 29 LinkedList<ApplicationListener<?>> allListeners = new LinkedList(); 30 Iterator var2 = this.applicationListeners.iterator(); 31 32 while(var2.hasNext()) { 33 ApplicationListener<?> listener = (ApplicationListener)var2.next(); 34 allListeners.add(listener); 35 } 36 37 if (!this.applicationListenerBeans.isEmpty()) { 38 BeanFactory beanFactory = AbstractApplicationEventMulticaster.this.getBeanFactory(); 39 Iterator var8 = this.applicationListenerBeans.iterator(); 40 41 while(var8.hasNext()) { 42 String listenerBeanName = (String)var8.next(); 43 44 try { 45 ApplicationListener<?> listenerx = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class); 46 if (this.preFiltered || !allListeners.contains(listenerx)) { 47 allListeners.add(listenerx); 48 } 49 } catch (NoSuchBeanDefinitionException var6) { 50 ; 51 } 52 } 53 } 54 55 AnnotationAwareOrderComparator.sort(allListeners); 56 return allListeners; 57 } 58 } 59 //略... 60 }
上述方法定義在SimpleApplicationEventMulticaster父類AbstractApplicationEventMulticaster中。關鍵代碼爲this.defaultRetriever.applicationListeners.add(listener);,這是一個內部類,用來保存全部的監聽器。也就是在這一步,將spring.factories中的監聽器傳遞到SimpleApplicationEventMulticaster中。咱們如今知道EventPublishingRunListener中有一個廣播器SimpleApplicationEventMulticaster,SimpleApplicationEventMulticaster廣播器中又存放全部的監聽器。
啓動監聽器
咱們上面一步經過getRunListeners
方法獲取的監聽器爲EventPublishingRunListener,從名字能夠看出是啓動事件發佈監聽器,主要用來發布啓動事件。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { private final SpringApplication application; private final String[] args; private final SimpleApplicationEventMulticaster initialMulticaster;
咱們先來看看SpringApplicationRunListener這個接口
package org.springframework.boot; public interface SpringApplicationRunListener { // 在run()方法開始執行時,該方法就當即被調用,可用於在初始化最先期時作一些工做 void starting(); // 當environment構建完成,ApplicationContext建立以前,該方法被調用 void environmentPrepared(ConfigurableEnvironment environment); // 當ApplicationContext構建完成時,該方法被調用 void contextPrepared(ConfigurableApplicationContext context); // 在ApplicationContext完成加載,但沒有被刷新前,該方法被調用 void contextLoaded(ConfigurableApplicationContext context); // 在ApplicationContext刷新並啓動後,CommandLineRunners和ApplicationRunner未被調用前,該方法被調用 void started(ConfigurableApplicationContext context); // 在run()方法執行完成前該方法被調用 void running(ConfigurableApplicationContext context); // 當應用運行出錯時該方法被調用 void failed(ConfigurableApplicationContext context, Throwable exception); }
SpringApplicationRunListener接口在Spring Boot 啓動初始化的過程當中各類狀態時執行,咱們也能夠添加本身的監聽器,在SpringBoot初始化時監聽事件執行自定義邏輯,咱們先來看看SpringBoot啓動時第一個啓動事件listeners.starting():
@Override public void starting() { //關鍵代碼,先建立application啓動事件`ApplicationStartingEvent` this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); }
這裏先建立了一個啓動事件ApplicationStartingEvent,咱們繼續跟進SimpleApplicationEventMulticaster,有個核心方法:
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); //經過事件類型ApplicationStartingEvent獲取對應的監聽器 for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { //獲取線程池,若是爲空則同步處理。這裏線程池爲空,還未沒初始化。 Executor executor = getTaskExecutor(); if (executor != null) { //異步發送事件 executor.execute(() -> invokeListener(listener, event)); } else { //同步發送事件 invokeListener(listener, event); } } }
這裏會根據事件類型ApplicationStartingEvent
獲取對應的監聽器,在容器啓動以後執行響應的動做,有以下4種監聽器:
咱們選擇springBoot 的日誌監聽器來進行講解,核心代碼以下:
@Override public void onApplicationEvent(ApplicationEvent event) { //在springboot啓動的時候 if (event instanceof ApplicationStartedEvent) { onApplicationStartedEvent((ApplicationStartedEvent) event); } //springboot的Environment環境準備完成的時候 else if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event); } //在springboot容器的環境設置完成之後 else if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent((ApplicationPreparedEvent) event); } //容器關閉的時候 else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event) .getApplicationContext().getParent() == null) { onContextClosedEvent(); } //容器啓動失敗的時候 else if (event instanceof ApplicationFailedEvent) { onApplicationFailedEvent(); } }
由於咱們的事件類型爲ApplicationEvent
,因此會執行onApplicationStartedEvent((ApplicationStartedEvent) event);
。springBoot會在運行過程當中的不一樣階段,發送各類事件,來執行對應監聽器的對應方法。
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
跟進去該方法:
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { //獲取對應的ConfigurableEnvironment ConfigurableEnvironment environment = getOrCreateEnvironment(); //配置 configureEnvironment(environment, applicationArguments.getSourceArgs()); //發佈環境已準備事件,這是第二次發佈事件 listeners.environmentPrepared(environment); bindToSpringApplication(environment); ConfigurationPropertySources.attach(environment); return environment; }
來看一下getOrCreateEnvironment()
方法,前面已經提到,environment
已經被設置了servlet
類型,因此這裏建立的是環境對象是StandardServletEnvironment
。
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } if (this.webApplicationType == WebApplicationType.SERVLET) { return new StandardServletEnvironment(); } return new StandardEnvironment(); }
接下來看一下listeners.environmentPrepared(environment);
,上面已經提到了,這裏是第二次發佈事件。什麼事件呢?來看一下根據事件類型獲取到的監聽器:
主要來看一下ConfigFileApplicationListener
,該監聽器很是核心,主要用來處理項目配置。項目中的 properties 和yml文件都是其內部類所加載。具體來看一下:
首先仍是會去讀spring.factories
文件,List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
獲取的處理類有如下四種:
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
在執行完上述三個監聽器流程後,ConfigFileApplicationListener
會執行該類自己的邏輯。由其內部類Loader
加載項目制定路徑下的配置文件:
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
至此,項目的變量配置已所有加載完畢,來一塊兒看一下:
這裏一共6個配置文件,取值順序由上到下。也就是說前面的配置變量會覆蓋後面同名的配置變量。項目配置變量的時候須要注意這點。
context = createApplicationContext();
繼續跟進該方法:
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; 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); }
這裏建立容器的類型 仍是根據webApplicationType
進行判斷的,該類型爲SERVLET
類型,因此會經過反射裝載對應的字節碼,也就是AnnotationConfigServletWebServerApplicationContext
這一步主要是在容器刷新以前的準備動做。包含一個很是關鍵的操做:將啓動類注入容器,爲後續開啓自動化配置奠基基礎。
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
繼續跟進該方法:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //設置容器環境,包括各類變量 context.setEnvironment(environment); //執行容器後置處理 postProcessApplicationContext(context); //執行容器中的ApplicationContextInitializer(包括 spring.factories和自定義的實例) applyInitializers(context); //發送容器已經準備好的事件,通知各監聽器 listeners.contextPrepared(context); //註冊啓動參數bean,這裏將容器指定的參數封裝成bean,注入容器 context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); //設置banner if (printedBanner != null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } //獲取咱們的啓動類指定的參數,能夠是多個 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); //加載咱們的啓動類,將啓動類注入容器 load(context, sources.toArray(new Object[0])); //發佈容器已加載事件。 listeners.contextLoaded(context); }
調用初始化器
protected void applyInitializers(ConfigurableApplicationContext context) { // 1. 從SpringApplication類中的initializers集合獲取全部的ApplicationContextInitializer for (ApplicationContextInitializer initializer : getInitializers()) { // 2. 循環調用ApplicationContextInitializer中的initialize方法 Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
這裏終於用到了在建立SpringApplication實例時設置的初始化器了,依次對它們進行遍歷,並調用initialize方法。咱們也能夠自定義初始化器,並實現initialize方法,而後放入META-INF/spring.factories配置文件中Key爲:org.springframework.context.ApplicationContextInitializer的value中,這裏咱們自定義的初始化器就會被調用,是咱們項目初始化的一種方式
加載啓動指定類(重點)
你們先回到文章最開始看看,在建立SpringApplication實例時,先將HelloWorldMainApplication.class存儲在this.primarySources屬性中,如今就是用到這個屬性的時候了,咱們來看看getAllSources()
public Set<Object> getAllSources() { Set<Object> allSources = new LinkedHashSet(); if (!CollectionUtils.isEmpty(this.primarySources)) { //獲取primarySources屬性,也就是以前存儲的HelloWorldMainApplication.class allSources.addAll(this.primarySources); } if (!CollectionUtils.isEmpty(this.sources)) { allSources.addAll(this.sources); } return Collections.unmodifiableSet(allSources); }
很明顯,獲取了this.primarySources屬性,也就是咱們的啓動類HelloWorldMainApplication.class,咱們接着看load(context, sources.toArray(new Object[0]));
protected void load(ApplicationContext context, Object[] sources) { 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(); } private int load(Class<?> source) { if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { // Any GroovyLoaders added in beans{} DSL can contribute beans here GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class); load(loader); } if (isComponent(source)) { //以註解的方式,將啓動類bean信息存入beanDefinitionMap,也就是將HelloWorldMainApplication.class存入了beanDefinitionMap this.annotatedReader.register(source); return 1; } return 0; }
啓動類
HelloWorldMainApplication.class被加載到 beanDefinitionMap中,後續該啓動類將做爲開啓自動化配置的入口,後面一篇文章我會詳細的分析,啓動類是如何加載,以及自動化配置開啓的詳細流程。
通知監聽器,容器已準備就緒
listeners.contextLoaded(context);
主仍是針對一些日誌等監聽器的響應處理。
執行到這裏,springBoot相關的處理工做已經結束,接下的工做就交給了spring。咱們來看看refreshContext(context);
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); //調用建立的容器applicationContext中的refresh()方法 ((AbstractApplicationContext)applicationContext).refresh(); } public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { /** * 刷新上下文環境 */ prepareRefresh(); /** * 初始化BeanFactory,解析XML,至關於以前的XmlBeanFactory的操做, */ ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); /** * 爲上下文準備BeanFactory,即對BeanFactory的各類功能進行填充,如經常使用的註解@Autowired @Qualifier等 * 添加ApplicationContextAwareProcessor處理器 * 在依賴注入忽略實現*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等 * 註冊依賴,如一個bean的屬性中含有ApplicationEventPublisher(beanFactory),則會將beanFactory的實例注入進去 */ prepareBeanFactory(beanFactory); try { /** * 提供子類覆蓋的額外處理,即子類處理自定義的BeanFactoryPostProcess */ postProcessBeanFactory(beanFactory); /** * 激活各類BeanFactory處理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor * 執行對應的postProcessBeanDefinitionRegistry方法 和 postProcessBeanFactory方法 */ invokeBeanFactoryPostProcessors(beanFactory); /** * 註冊攔截Bean建立的Bean處理器,即註冊BeanPostProcessor,不是BeanFactoryPostProcessor,注意二者的區別 * 注意,這裏僅僅是註冊,並不會執行對應的方法,將在bean的實例化時執行對應的方法 */ registerBeanPostProcessors(beanFactory); /** * 初始化上下文中的資源文件,如國際化文件的處理等 */ initMessageSource(); /** * 初始化上下文事件廣播器,並放入applicatioEventMulticaster,如ApplicationEventPublisher */ initApplicationEventMulticaster(); /** * 給子類擴展初始化其餘Bean */ onRefresh(); /** * 在全部bean中查找listener bean,而後註冊到廣播器中 */ registerListeners(); /** * 設置轉換器 * 註冊一個默認的屬性值解析器 * 凍結全部的bean定義,說明註冊的bean定義將不能被修改或進一步的處理 * 初始化剩餘的非惰性的bean,即初始化非延遲加載的bean */ finishBeanFactoryInitialization(beanFactory); /** * 經過spring的事件發佈機制發佈ContextRefreshedEvent事件,以保證對應的監聽器作進一步的處理 * 即對那種在spring啓動後須要處理的一些類,這些類實現了ApplicationListener<ContextRefreshedEvent>, * 這裏就是要觸發這些類的執行(執行onApplicationEvent方法) * 另外,spring的內置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent * 完成初始化,通知生命週期處理器lifeCycleProcessor刷新過程,同時發出ContextRefreshEvent通知其餘人 */ finishRefresh(); } finally { resetCommonCaches(); } } }
refresh
方法在spring整個源碼體系中舉足輕重,是實現 ioc 和 aop的關鍵。我以前也有文章分析過這個過程,你們能夠去看看
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { }
擴展接口,設計模式中的模板方法,默認爲空實現。若是有自定義需求,能夠重寫該方法。好比打印一些啓動結束log,或者一些其它後置處理。
public void started(ConfigurableApplicationContext context) { //這裏就是獲取的EventPublishingRunListener Iterator var2 = this.listeners.iterator(); while(var2.hasNext()) { SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next(); //執行EventPublishingRunListener的started方法 listener.started(context); } } public void started(ConfigurableApplicationContext context) { //建立ApplicationStartedEvent事件,而且發佈事件 //咱們看到是執行的ConfigurableApplicationContext這個容器的publishEvent方法,和前面的starting是不一樣的 context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context)); }
獲取EventPublishingRunListener監聽器,並執行其started方法,而且將建立的Spring容器傳進去了,建立一個ApplicationStartedEvent事件,並執行ConfigurableApplicationContext 的publishEvent方法,也就是說這裏是在Spring容器中發佈事件,並非在SpringApplication中發佈事件,和前面的starting是不一樣的,前面的starting是直接向SpringApplication中的11個監聽器發佈啓動事件。
咱們再來看看最後一步callRunners(context, applicationArguments);
private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<Object>(); //獲取容器中全部的ApplicationRunner的Bean實例 runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); //獲取容器中全部的CommandLineRunner的Bean實例 runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<Object>(runners)) { if (runner instanceof ApplicationRunner) { //執行ApplicationRunner的run方法 callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { //執行CommandLineRunner的run方法 callRunner((CommandLineRunner) runner, args); } } }
若是是ApplicationRunner的話,則執行以下代碼:
private void callRunner(ApplicationRunner runner, ApplicationArguments args) { try { runner.run(args); } catch (Exception var4) { throw new IllegalStateException("Failed to execute ApplicationRunner", var4); } }
若是是CommandLineRunner的話,則執行以下代碼:
private void callRunner(CommandLineRunner runner, ApplicationArguments args) { try { runner.run(args.getSourceArgs()); } catch (Exception var4) { throw new IllegalStateException("Failed to execute CommandLineRunner", var4); } }
咱們也能夠自定義一些ApplicationRunner或者CommandLineRunner,實現其run方法,並注入到Spring容器中,在SpringBoot啓動完成後,會執行全部的runner的run方法
至此,SpringApplication大概分析了一遍,還有不少細節和核心留在下面文章中講。