https://blog.csdn.net/dm_vincent/article/details/76735888php
關於Spring Boot,已經有不少介紹其如何使用的文章了,本文從源代碼(基於Spring-boot 1.5.6)的角度來看看Spring Boot的啓動過程究竟是怎麼樣的,爲什麼以往紛繁複雜的配置到現在能夠這麼簡便。css
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
以上的代碼就是經過Spring Initializr配置生成的一個最簡單的Web項目(只引入了Web功能)的入口方法。這個想必只要是接觸過Spring Boot都會很熟悉。簡單的方法背後掩藏的是Spring Boot在啓動過程當中的複雜性,本文的目的就是一探這裏面的究竟。java
而在看這個方法的實現以前,須要看看@SpringBootApplication這個註解的功能:react
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication { // ... }
很明顯的,這個註解就是三個經常使用在一塊兒的註解@SpringBootConfiguration,@EnableAutoConfiguration以及@ComponentScan的組合,並無什麼高深的地方。web
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }
這個註解實際上和@Configuration有相同的做用,配備了該註解的類就可以以JavaConfig的方式完成一些配置,能夠再也不使用XML配置。spring
顧名思義,這個註解完成的是自動掃描的功能,至關於Spring XML配置文件中的:數組
<context:component-scan>
可使用basePackages屬性指定要掃描的包,以及掃描的條件。若是不設置的話默認掃描@ComponentScan註解所在類的同級類和同級目錄下的全部類,因此對於一個Spring Boot項目,通常會把入口類放在頂層目錄中,這樣就可以保證源碼目錄下的全部類都可以被掃描到。springboot
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({EnableAutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
這個註解是讓Spring Boot的配置可以如此簡化的關鍵性註解。目前知道這個註解的做用就能夠了,關於自動配置再也不本文討論範圍內,後面若是有機會另起文章專門分析這個自動配置的實現原理。app
介紹完了入口類,下面開始分析關鍵方法:框架
SpringApplication.run(DemoApplication.class, args);
相應實現:
// 參數對應的就是DemoApplication.class以及main方法中的args public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args); } // 最終運行的這個重載方法 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
它實際上會構造一個SpringApplication的實例,而後運行它的run方法:
// 構造實例
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = deduceWebApplicationType(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
在構造函數中,主要作了4件事情:
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" };
可能會出現三種結果:
setInitializers((Collection) 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 { 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)); 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部分定義以下所示:
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
所以這兩個類名會被讀取出來,而後放入到集合中,準備開始下面的實例化操做:
// 關鍵參數:
// type: org.springframework.context.ApplicationContextInitializer.class
// names: 上一步獲得的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); Assert.isAssignable(type, instanceClass); Constructor<?> constructor = instanceClass .getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); 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> { /** * Initialize the given application context. * @param applicationContext the application to configure */ void initialize(C applicationContext); }
根據類文檔,這個接口的主要功能是:
在Spring上下文被刷新以前進行初始化的操做。典型地好比在Web應用中,註冊Property Sources或者是激活Profiles。Property Sources比較好理解,就是配置文件。Profiles是Spring爲了在不一樣環境下(如DEV,TEST,PRODUCTION等),加載不一樣的配置項而抽象出來的一個實體。
設置完了初始化器,下面開始設置監聽器:
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
一樣地,監聽器也是一個新概念,仍是從代碼入手:
// 這裏的入參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:
# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer
至於ApplicationListener接口,它是Spring框架中一個至關基礎的接口了,代碼以下:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(E event); }
這個接口基於JDK中的EventListener接口,實現了觀察者模式。對於Spring框架的觀察者模式實現,它限定感興趣的事件類型須要是ApplicationEvent類型的子類,而這個類一樣是繼承自JDK中的EventObject類。
this.mainApplicationClass = deduceMainApplicationClass();
這個方法的實現有點意思:
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
它經過構造一個運行時異常,經過異常棧中方法名爲main的棧幀來獲得入口類的名字。
至此,對於SpringApplication實例的初始化過程就結束了。
完成了實例化,下面開始調用run方法:
// 運行run方法 public ConfigurableApplicationContext run(String... args) { // 計時工具 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); // 設置java.awt.headless系統屬性爲true - 沒有圖形化界面 configureHeadlessProperty(); // KEY 1 - 獲取SpringApplicationRunListeners SpringApplicationRunListeners listeners = getRunListeners(args); // 發出開始執行的事件 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); // KEY 2 - 根據SpringApplicationRunListeners以及參數來準備環境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 準備Banner打印器 - 就是啓動Spring Boot的時候打印在console上的ASCII藝術字體 Banner printedBanner = printBanner(environment); // KEY 3 - 建立Spring上下文 context = createApplicationContext(); // 準備異常報告器 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); // KEY 4 - Spring上下文前置處理 prepareContext(context, environment, listeners, applicationArguments, printedBanner); // KEY 5 - Spring上下文刷新 refreshContext(context); // KEY 6 - Spring上下文後置處理 afterRefresh(context, applicationArguments); // 發出結束執行的事件 listeners.finished(context, null); // 中止計時器 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, exceptionReporters, ex); throw new IllegalStateException(ex); } }
這個run方法包含的內容也是有點多的,根據上面列舉出的關鍵步驟逐個進行分析:
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); }
這裏仍然利用了getSpringFactoriesInstances方法來獲取實例:
// 這裏的入參: // type: SpringApplicationRunListener.class // parameterTypes: new Class<?>[] { SpringApplication.class, String[].class }; // args: SpringApplication實例自己 + main方法傳入的args 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; }
因此這裏仍是故技重施,從META-INF/spring.factories中讀取Key爲org.springframework.boot.SpringApplicationRunListener的Values:
好比在spring-boot包中的定義的spring.factories:
# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener
咱們來看看這個EventPublishingRunListener是幹嗎的:
/** * {@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 */ public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { // ... }
從類文檔能夠看出,它主要是負責發佈SpringApplicationEvent事件的,它會利用一個內部的ApplicationEventMulticaster在上下文實際被刷新以前對事件進行處理。至於具體的應用場景,後面用到的時候再來分析。
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); if (!this.webEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } return environment; }
配置環境的方法:
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { configurePropertySources(environment, args); configureProfiles(environment, args); }
因此這裏實際上也包含了兩個步驟:
具體實現這裏就不展開了,代碼也比較直觀。
對於Web應用而言,獲得的environment變量是一個StandardServletEnvironment的實例。獲得實例後,會調用前面RunListeners中的environmentPrepared方法:
@Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( this.application, this.args, environment)); }
在這裏,定義的廣播器就派上用場了,它會發佈一個ApplicationEnvironmentPreparedEvent事件。
那麼有發佈就有監聽,在構建SpringApplication實例的時候不是初始化過一些ApplicationListeners嘛,其中的Listener就可能會監聽ApplicationEnvironmentPreparedEvent事件,而後進行相應處理。
因此這裏SpringApplicationRunListeners的用途和目的也比較明顯了,它其實是一個事件中轉器,它可以感知到Spring Boot啓動過程當中產生的事件,而後有選擇性的將事件進行中轉。爲什麼是有選擇性的,看看它的實現就知道了:
@Override public void contextPrepared(ConfigurableApplicationContext context) { }
它的contextPrepared方法實現爲空,沒有利用內部的initialMulticaster進行事件的派發。所以即使是外部有ApplicationListener對這個事件有興趣,也是沒有辦法監聽到的。
那麼既然有事件的轉發,是誰在監聽這些事件呢,在這個類的構造器中交待了:
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); } }
前面在構建SpringApplication實例過程當中設置的監聽器在這裏被逐個添加到了initialMulticaster對應的ApplicationListener列表中。因此當initialMulticaster調用multicastEvent方法時,這些Listeners中定義的相應方法就會被觸發了。
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { contextClass = Class.forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass); } // WEB應用的上下文類型 public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
這個上下文類型的類圖以下所示:
這也是至關複雜的一個類圖了,若是能把這張圖中的各個類型的做用弄清楚,估計也是一個Spring大神了 :)
對於咱們的Web應用,上下文類型就是DEFAULT_WEB_CONTEXT_CLASS。
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 將環境和上下文關聯起來 context.setEnvironment(environment); // 爲上下文配置Bean生成器以及資源加載器(若是它們非空) postProcessApplicationContext(context); // 調用初始化器 applyInitializers(context); // 觸發Spring Boot啓動過程的contextPrepared事件 listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 添加兩個Spring Boot中的特殊單例Beans - springApplicationArguments以及springBootBanner context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } // 加載sources - 對於DemoApplication而言,這裏的sources集合只包含了它一個class對象 Set<Object> sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 加載動做 - 構造BeanDefinitionLoader並完成Bean定義的加載 load(context, sources.toArray(new Object[sources.size()])); // 觸發Spring Boot啓動過程的contextLoaded事件 listeners.contextLoaded(context); }
關鍵步驟:
配置Bean生成器以及資源加載器(若是它們非空):
protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.beanNameGenerator != null) { context.getBeanFactory().registerSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context) .setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context) .setClassLoader(this.resourceLoader.getClassLoader()); } } }
調用初始化器
protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
這裏終於用到了在建立SpringApplication實例時設置的初始化器了,依次對它們進行遍歷,並調用initialize方法。
private void refreshContext(ConfigurableApplicationContext context) { // 因爲這裏須要調用父類一系列的refresh操做,涉及到了不少核心操做,所以耗時會比較長,本文不作具體展開 refresh(context); // 註冊一個關閉容器時的鉤子函數 if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } // 調用父類的refresh方法完成容器刷新的基礎操做 protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext)applicationContext).refresh(); }
註冊關閉容器時的鉤子函數的默認實現是在AbstractApplicationContext類中:
public void registerShutdownHook() { if(this.shutdownHook == null) { this.shutdownHook = new Thread() { public void run() { synchronized(AbstractApplicationContext.this.startupShutdownMonitor) { AbstractApplicationContext.this.doClose(); } } }; Runtime.getRuntime().addShutdownHook(this.shutdownHook); } }
若是沒有提供自定義的shutdownHook,那麼會生成一個默認的,並添加到Runtime中。默認行爲就是調用它的doClose方法,完成一些容器銷燬時的清理工做。
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { callRunners(context, args); } private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<Object>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<Object>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } } private void callRunner(ApplicationRunner runner, ApplicationArguments args) { try { (runner).run(args); } catch (Exception ex) { throw new IllegalStateException("Failed to execute ApplicationRunner", ex); } } private void callRunner(CommandLineRunner runner, ApplicationArguments args) { try { (runner).run(args.getSourceArgs()); } catch (Exception ex) { throw new IllegalStateException("Failed to execute CommandLineRunner", ex); } }
所謂的後置操做,就是在容器完成刷新後,依次調用註冊的Runners。Runners能夠是兩個接口的實現類:
這兩個接口有什麼區別呢:
/** * Interface used to indicate that a bean should <em>run</em> when it is contained within * a {@link SpringApplication}. Multiple {@link ApplicationRunner} beans can be defined * within the same application context and can be ordered using the {@link Ordered} * interface or {@link Order @Order} annotation. * * @author Phillip Webb * @since 1.3.0 * @see CommandLineRunner */ public interface ApplicationRunner { /** * Callback used to run the bean. * @param args incoming application arguments * @throws Exception on error */ void run(ApplicationArguments args) throws Exception; } /** * Interface used to indicate that a bean should <em>run</em> when it is contained within * a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined * within the same application context and can be ordered using the {@link Ordered} * interface or {@link Order @Order} annotation. * <p> * If you need access to {@link ApplicationArguments} instead of the raw String array * consider using {@link ApplicationRunner}. * * @author Dave Syer * @see ApplicationRunner */ public interface CommandLineRunner { /** * Callback used to run the bean. * @param args incoming main method arguments * @throws Exception on error */ void run(String... args) throws Exception; }
其實沒有什麼不一樣之處,除了接口中的run方法接受的參數類型是不同的之外。一個是封裝好的ApplicationArguments類型,另外一個是直接的String不定長數組類型。所以根據須要選擇相應的接口實現便可。
至此,SpringApplication的run方法就分析完畢了。
本文分析了Spring Boot啓動時的關鍵步驟,主要包含如下兩個方面:
SpringApplication實例的構建過程
其中主要涉及到了初始化器(Initializer)以及監聽器(Listener)這兩大概念,它們都經過META-INF/spring.factories完成定義。
SpringApplication實例run方法的執行過程
其中主要有一個SpringApplicationRunListeners的概念,它做爲Spring Boot容器初始化時各階段事件的中轉器,將事件派發給感興趣的Listeners(在SpringApplication實例的構建過程當中獲得的)。這些階段性事件將容器的初始化過程給構造起來,提供了比較強大的可擴展性。
若是從可擴展性的角度出發,應用開發者能夠在Spring Boot容器的啓動階段,擴展哪些內容呢: