1. 環境,程序入口html
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } @RestController public class RootController { public static final String PATH_ROOT = "/"; @RequestMapping(PATH_ROOT) public String welcome() { return "Welcome!"; } }
這裏的幾行代碼就是Spring BootWeb程序,入口是main方法,當訪問url的path部分爲」/」時,返回字符串」Welcome!」。java
解釋:在這個main方法中,調用了SpringApplication的靜態run方法,並將Application類對象和main方法的參數args做爲參數傳遞了進去。web
public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[] { source }, args); } public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); }
而後是一個使用了兩個Spring註解的RootController類,咱們在main方法中,沒有直接使用這個類。spring
2. 構造SpringApplication對象數組
public SpringApplication(Object... sources) { initialize(sources); } private void initialize(Object[] sources) { // 爲成員變量sources賦值 if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
構造函數中調用initialize方法,初始化SpringApplication對象的成員變量sources,webEnvironment,initializers,listeners,mainApplicationClass。sources的賦值比較簡單,就是咱們傳給SpringApplication.run方法的參數。服務器
2.1 首先是webEnvironmentapp
private boolean webEnvironment; private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private void initialize(Object[] sources) { ... // 爲成員變量webEnvironment賦值 this.webEnvironment = deduceWebEnvironment(); ... } private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; }
webEnvironment是一個boolean,該成員變量用來表示當前應用程序是否是一個Web應用程序。那麼怎麼決定當前應用程序是否Web應用程序呢,是經過在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES這個數組中所包含的類,若是存在那麼當前程序便是一個Web應用程序,反之則否則。less
2.2 成員變量initializers:ide
initializers是一個ApplicationContextInitializer類型對象的集合。 因此,ApplicationContextInitializer是一個能夠用來初始化ApplicationContext的接口。函數
private List<ApplicationContextInitializer<?>> initializers; private void initialize(Object[] sources) { ... // 爲成員變量initializers賦值 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); ... } public void setInitializers( Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<ApplicationContextInitializer<?>>(); this.initializers.addAll(initializers); }
關鍵是調用getSpringFactoriesInstances(ApplicationContextInitializer.class),來獲取ApplicationContextInitializer類型對象的列表。
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; }
首先經過調用SpringFactoriesLoader.loadFactoryNames(type, classLoader)來獲取全部Spring Factories的名字,而後調用createSpringFactoriesInstances方法根據讀取到的名字建立對象。最後會將建立好的對象列表排序並返回。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); while (urls.hasMoreElements()) { 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 ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
從一個名字叫spring.factories的資源文件中,讀取key爲org.springframework.context.ApplicationContextInitializer的value。
spring.factories的部份內容以下:
如下內容摘自spring-boot-1.3.3.RELEASE.jar中的資源文件META-INF/spring.factories # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer
接下來會調用createSpringFactoriesInstances來建立ApplicationContextInitializer實例。
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.getConstructor(parameterTypes); T instance = (T) constructor.newInstance(args); instances.add(instance); } catch (Throwable ex) { throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex); } } return instances; }
SpringApplication對象的成員變量initalizers就被初始化爲,ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer這四個類的對象組成的list。
ApplicationContextInitializer說明如圖:
2.3 成員變量listeners
如下代碼摘自:org.springframework.boot.SpringApplication private List<ApplicationListener<?>> listeners; private void initialize(Object[] sources) { ... // 爲成員變量listeners賦值 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); ... } public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { this.listeners = new ArrayList<ApplicationListener<?>>(); this.listeners.addAll(listeners); }
listeners成員變量,是一個ApplicationListener<?>類型對象的集合。能夠看到獲取該成員變量內容使用的是跟成員變量initializers同樣的方法,只不過傳入的類型從ApplicationContextInitializer.class變成了ApplicationListener.class。
下圖畫出了加載的ApplicationListener,並說明了他們的做用。
2.4 mainApplicationClass
如下代碼摘自:org.springframework.boot.SpringApplication private Class<?> mainApplicationClass; private void initialize(Object[] sources) { ... // 爲成員變量mainApplicationClass賦值 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; }
在deduceMainApplicationClass方法中,經過獲取當前調用棧,找到入口方法main所在的類,並將其複製給SpringApplication對象的成員變量mainApplicationClass。例子中mainApplicationClass是本身編寫的Application類。
SpringApplication對象的run方法
通過上面的初始化過程,咱們已經有了一個SpringApplication對象,根據SpringApplication類的靜態run方法一節中的分析,接下來會調用SpringApplication對象的run方法。咱們接下來就分析這個對象的run方法。
如下代碼摘自:org.springframework.boot.SpringApplication public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.started(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); context = createAndRefreshContext(listeners, applicationArguments); 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, ex); throw new IllegalStateException(ex); } } 可變個數參數args就是咱們整個應用程序的入口main方法的參數,在咱們的例子中,參數個數爲零。 StopWatch是來自org.springframework.util的工具類,能夠用來方便的記錄程序的運行時間。
SpringApplication對象的run方法建立並刷新ApplicationContext,算是開始進入正題了。下面按照執行順序,介紹該方法所作的工做。
headless模式
如下代碼摘自:org.springframework.boot.SpringApplication private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless"; private boolean headless = true; public ConfigurableApplicationContext run(String... args) { ... //設置headless模式 configureHeadlessProperty(); ... } private void configureHeadlessProperty() { System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty( SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless))); }
其實是就是設置系統屬性java.awt.headless,在咱們的例子中該屬性會被設置爲true,由於咱們開發的是服務器程序,通常運行在沒有顯示器和鍵盤的環境。
SpringApplicationRunListeners
如下代碼摘自:org.springframework.boot.SpringApplication public ConfigurableApplicationContext run(String... args) { ... SpringApplicationRunListeners listeners = getRunListeners(args); listeners.started(); /** * 建立並刷新ApplicationContext * context = createAndRefreshContext(listeners, applicationArguments); **/ listeners.finished(context, null); ... } private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); }
run方法中,加載了一系列SpringApplicationRunListener對象,在建立和更新ApplicationContext方法先後分別調用了listeners對象的started方法和finished方法, 並在建立和刷新ApplicationContext時,將listeners做爲參數傳遞到了createAndRefreshContext方法中,以便在建立和刷新ApplicationContext的不一樣階段,調用listeners的相應方法以執行操做。因此,所謂的SpringApplicationRunListeners實際上就是在SpringApplication對象的run方法執行的不一樣階段,去執行一些操做,而且這些操做是可配置的。
同時,能夠看到,加載SpringApplicationRunListener時,使用的是跟加載ApplicationContextInitializer和ApplicationListener時同樣的方法。那麼加載了什麼,就能夠從spring.factories文件中看到了:
如下內容摘自spring-boot-1.3.3.RELEASE.jar中的資源文件META-INF/spring.factories # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener
能夠看到,在咱們的例子中加載的是org.springframework.boot.context.event.EventPublishingRunListener。
SpringApplicationRunListener究竟作了點什麼工做了?
如下代碼摘自:org.springframework.boot.context.event.EventPublishingRunListener public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.multicaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<?> listener : application.getListeners()) { this.multicaster.addApplicationListener(listener); } } @Override public void started() { publishEvent(new ApplicationStartedEvent(this.application, this.args)); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { publishEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); } @Override public void contextPrepared(ConfigurableApplicationContext context) { registerApplicationEventMulticaster(context); } @Override public void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<?> listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } publishEvent(new ApplicationPreparedEvent(this.application, this.args, context)); } @Override public void finished(ConfigurableApplicationContext context, Throwable exception) { publishEvent(getFinishedEvent(context, exception)); }
EventPublishingRunListener在對象初始化時,將SpringApplication對象的成員變量listeners全都保存下來,而後在本身的public方法被調用時,發佈相應的事件,或執行相應的操做。能夠說這個RunListener是在SpringApplication對象的run方法執行到不一樣的階段時,發佈相應的event給SpringApplication對象的成員變量listeners中記錄的事件監聽器。
下圖是SpringApplicationRunListeners相關的類結構
同時,能夠看到,加載SpringApplicationRunListener時,使用的是跟加載ApplicationContextInitializer和ApplicationListener時同樣的方法。那麼加載了什麼,就能夠從spring.factories文件中看到了: