spring boot 啓動記錄

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。

相關文章
相關標籤/搜索