SpringBoot是Spring主推的基於"習慣優於配置"的原則,快速搭建應用的框架,它實現了jar in jar的加載方式。java
spring boot應用打包以後,生成一個fat jar,裏面包含了應用依賴的jar包,還有Spring boot loader相關的類react
其中META-INF/MANIFEST.MF文件下的兩個Class:
Main-Class是org.springframework.boot.loader.JarLauncher ,這個是jar啓動的Main函數。
Start-Class是應用本身的Main函數。git
程序啓動時JarLauncher先找到本身所在的jar,而後建立了一個Archive(在spring boot裏,一個archive能夠是一個jar:JarFileArchive,也能夠是一個文件目錄:ExplodedArchive)。再獲取到<工程>.jar/lib下面的全部jar文件。<工程>.jar既fat jar。
獲取到全部Archive的URL以後,會構造一個自定義的ClassLoader:LaunchedURLClassLoader。
它從MANIFEST.MF裏讀取到Start-Class,建立一個新的線程來啓動應用的Main函數。github
工程以fat jar運行時,應用的main函數的ClassLoader是LaunchedURLClassLoader,它的parent是SystemClassLoader。
而且LaunchedURLClassLoader的urls是 fat jar裏的BOOT-INF/classes!/目錄和BOOT-INF/lib裏的全部jar。
SystemClassLoader的urls是fat jar自己。web
工程在IDE裏啓動,由於依賴的Jar都讓IDE放到classpath裏了,因此Spring boot直接啓動了。
Spring的ClassLoader直接是SystemClassLoader。ClassLoader的urls包含所有的jar和本身的target/classesspring
工程在一個開放目錄下啓動Spring boot啓動。所謂的開放目錄就是把fat jar解壓,而後直接啓動應用。
Spring boot會判斷當前是否在一個目錄裏,若是是的,則構造一個ExplodedArchive(前面在jar裏時是JarFileArchive),後面的啓動流程相似fat jar的。apache
執行應用的main函數的ClassLoader是LaunchedURLClassLoader,它的parent是SystemClassLoader。
LaunchedURLClassLoader的urls是解壓目錄裏的BOOT-INF/classes/和/BOOT-INF/lib/下面的jar包。
SystemClassLoader的urls只有當前目錄
目錄形式會有更好的兼容性。編程
Start-Class的代碼裏基本上就一句話
SpringApplication.run(Application.class,args);tomcat
這個main方法中,調用了SpringApplication的靜態run方法,並將Application類對象和main方法的參數args做爲參數傳遞了進去。app
核心代碼以下:
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
一、SpringApplicationRunListeners (初始化監聽器)
二、配置參數和環境
三、打印Banner
四、建立ApplicationContext(),在裏面會判斷webApplicationType,而後具體Class.forName哪一個ApplicationContext(AnnotationConfigApplicationContext、 AnnotationConfigServletWebServerApplicationContext、 AnnotationConfigReactiveWebServerApplicationContext)
五、使用擴展機制加載其餘configure
六、準備context
七、刷新context
。。。。。。
Spring Boot裏用於解耦的擴展機制:Spring Factories。這種擴展機制其實是仿照Java中的SPI擴展機制來實現的。
其主要功能就是從指定的配置文件(SpringBoot的autoconfigure依賴包)中的META-INF/spring.factories加載配置。
在Spring中也有一種相似與Java SPI的加載機制。它從spring.factories文件中配置接口的實現類名稱,而後在程序中讀取這些配置文件並實例化。
這種自定義的SPI機制是Spring Boot Starter實現的基礎。
spring-core包裏定義了SpringFactoriesLoader類,這個類實現了檢索META-INF/spring.factories文件,並獲取指定接口的配置的功能。spring.factories的是經過Properties解析獲得的,因此咱們在寫文件中的內容都是安裝下面這種方式配置的:com.xxx.interface=com.xxx.classname
若是一個接口但願配置多個實現類,可使用','進行分割。
Factories機制可讓SDK或者Starter的使用只須要不多或者不須要進行配置,只須要在服務中引入咱們的jar包。
XXXAutoConfiguration上會根據@Conditional標註來判斷類會不會實例化
spring boot embeded container AutoConfiguration 代碼以下:
package org.springframework.boot.autoconfigure.web.embedded; import io.undertow.Undertow; import org.apache.catalina.startup.Tomcat; import org.apache.coyote.UpgradeProtocol; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.webapp.WebAppContext; import org.xnio.SslClientAuthMode; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; /** * {@link EnableAutoConfiguration Auto-configuration} for embedded servlet and reactive * web servers customizations. * * @author Phillip Webb * @since 2.0.0 */ @Configuration @EnableConfigurationProperties(ServerProperties.class) public class EmbeddedWebServerFactoryCustomizerAutoConfiguration { @ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class }) public static class TomcatWebServerFactoryCustomizerConfiguration { @Bean public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new TomcatWebServerFactoryCustomizer(environment, serverProperties); } } /** * Nested configuration if Jetty is being used. */ @Configuration @ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class }) public static class JettyWebServerFactoryCustomizerConfiguration { @Bean public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new JettyWebServerFactoryCustomizer(environment, serverProperties); } } /** * Nested configuration if Undertow is being used. */ @Configuration @ConditionalOnClass({ Undertow.class, SslClientAuthMode.class }) public static class UndertowWebServerFactoryCustomizerConfiguration { @Bean public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new UndertowWebServerFactoryCustomizer(environment, serverProperties); } } }
@conditionalonclass:某個class位於類路徑上,纔會實例化這個Bean ,根據上面的configuration,jar包裏若是能找到 Tomcat.class, UpgradeProtocol.class
就會實例化 TomcatWebServerFactoryCustomizerConfiguration
Java SPI:
咱們系統裏抽象的各個模塊,每每有不少不一樣的實現方案,好比日誌模塊的方案,xml解析模塊、jdbc模塊的方案等。面向的對象的設計裏,咱們通常推薦模塊之間基於接口編程,模塊之間不對實現類進行硬編碼。
一旦代碼裏涉及具體的實現類,就違反了可拔插的原則,若是須要替換一種實現,就須要修改代碼。爲了實如今模塊裝配的時候能不在程序裏動態指明,這就須要一種服務發現機制。java spi就是提供這樣的一個機制:爲某個接口尋找服務實現的機制。有點相似IOC的思想,就是將裝配的控制權移到程序以外,在模塊化設計中這個機制尤爲重要。
Java SPI約定:
當服務的提供者,提供了服務接口的一種實現以後,在jar包的META-INF/services/目錄裏同時建立一個以服務接口命名的文件。該文件裏就是實現該服務接口的具體實現類。而當外部程序裝配這個模塊的時候,就能經過該jar包META-INF/services/裏的配置文件找到具體的實現類名,並裝載實例化,完成模塊的注入。 基於這樣一個約定就能很好的找到服務接口的實現類,而不須要再代碼裏制定。jdk提供服務實現查找的一個工具類:java.util.ServiceLoader。