Spring Boot 原始碼分析
Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。通過這種方式,Boot致力於在蓬勃發展的快速應用開發領域(rapid application development)成為領導者。
Spring boot的特點
- 建立獨立的Spring應用程式
- 嵌入的Tomcat,無需部署WAR檔案
- 簡化Maven配置
- 自動配置Spring
- 提供生產就緒型功能,如指標,健康檢查和外部配置
- 絕對沒有程式碼生成和對XML沒有要求配置
Spring boot的優點
spring boot 可以支援你快速的開發出 restful 風格的微服務架構
自動化確實方便,做微服務再合適不過了,單一jar包部署和管理都非常方便。只要系統架構設計合理,大型專案也能用,加上nginx負載均衡,輕鬆實現橫向擴充套件
spring boot 要解決的問題, 精簡配置是一方面, 另外一方面是如何方便的讓spring生態圈和其他工具鏈整合(比如redis, email, elasticsearch)
1、專案初始化過程
springboot啟動類
SpringBoot的啟動很簡單,程式碼如下:
@SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
跟進去可以看到有兩步,一個是初始化,一個是run方法的執行:
1.1、SpringApplication初始化化過程:
SpringApplication的初始化大致分為以下的步驟:
- 判斷是否是web應用程式
- 從所有類中查詢META-INF/spring.factories檔案,載入其中的初始化類和監聽類。
- 查詢執行的主類 預設初始化Initializers都繼承自ApplicationContextInitializer。
SpringApplication構建函式:
public SpringApplication(ResourceLoader resourceLoader, Object... sources) { this.resourceLoader = resourceLoader; initialize(sources); }
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//是否是web應用程式。通過判斷應用程式中是否可以載入(class.forname)【"javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext"】這兩個類
this.webEnvironment = deduceWebEnvironment();
//設定初始化類:從配置檔案spring.factories中查詢所有的key=org.springframework.context.ApplicationContextInitializer的類【載入,初始化,排序】
//SpringFactoriesLoader:工廠載入機制
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//設定Listeners:從配置檔案spring.factories中查詢所有的key=org.springframework.context.ApplicationListener的類.【載入,初始化,排序】
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//從當前呼叫棧中,查詢主類
this.mainApplicationClass = deduceMainApplicationClass();
}
SpringFactoriesLoader工廠載入機制
Initializers和Listeners的載入過程都是使用到了SpringFactoriesLoader工廠載入機制。我們進入到getSpringFactoriesInstances這個方法中:
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
//獲取META-INF/spring.factories檔案中key為type型別的所有的類全限定名。注意是所有jar包內的。
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//通過上面獲取到的類的全限定名,這裡將會使用Class.forName載入類,並呼叫構造方法例項化類
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//根據類上的org.springframework.core.annotation.Order註解,排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
SpringFactoriesLoader.loadFactoryNames(type, classLoader));展示類方法載入的過程:
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);
}
}
ApplicationContextInitializer的類圖:
初始化ApplicationContextInitializer:
“org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer”
“org.springframework.boot.context.ContextIdApplicationContextInitializer”
“org.springframework.boot.context.config.DelegatingApplicationContextInitializer”
“org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer”
“org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer”
“org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer”
初始化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.liquibase.LiquibaseServiceLocatorApplicationListener”
“org.springframework.boot.logging.ClasspathLoggingApplicationListener”
“org.springframework.boot.logging.LoggingApplicationListener”
“org.springframework.boot.autoconfigure.BackgroundPreinitializer”
1.2、Run 方法
啟動run過程
- 註冊一個StopWatch,用於監控啟動過程
- 獲取監聽器SpringApplicationRunListener,用於springboot啟動過程中的事件廣播
- 設定環境變數environment
- 建立spring容器
- 建立FailureAnalyzers錯誤分析器,用於處理記錄啟動過程中的錯誤資訊
- 呼叫所有初始化類的initialize方法
- 初始化spring容器
- 執行ApplicationRunner和CommandLineRunner的實現類
- 啟動完成
public ConfigurableApplicationContext run(String... args) {
//stopWatch 用於簡單監聽run啟動過程
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//獲取監聽器。springboot中有一個SpringApplicationRunListener監聽器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.started();
try {
//下面兩句是載入屬性配置,執行完成後,所有的environment的屬性都會載入進來,包括application.properties和外部的屬性配置。
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//列印Banner
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
//錯誤分析器
analyzers = new FailureAnalyzers(context);
//主要是呼叫所有初始化類的initialize方法
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//初始化spring容器
refreshContext(context);
//主要是執行ApplicationRunner和CommandLineRunner的實現類
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, analyzers, ex);
throw new IllegalStateException(ex);
}
}
上述run過程廣泛應用了spring事件機制(主要是廣播)。上述程式碼中首先獲取SpringApplicationRunListeners。這就是在spring.factories檔案中配置的所有監聽器。然後整個run 過程使用了listeners的5個方法,每個方法對應一個事件Event:
starting() run方法執行的時候立馬執行;對應事件的型別是ApplicationStartedEvent
environmentPrepared() ApplicationContext建立之前並且環境資訊準備好的時候呼叫;對應事件的型別是ApplicationEnvironmentPreparedEvent
contextPrepared() ApplicationContext建立好並且在source載入之前呼叫一次;沒有具體的對應事件
contextLoaded() ApplicationContext建立並載入之後並在refresh之前呼叫;對應事件的型別是ApplicationPreparedEvent
finished() run方法結束之前呼叫;對應事件的型別是ApplicationReadyEvent或ApplicationFailedEven
SpringApplicationRunListeners是SpringApplicationRunListener的集合,SpringApplicationRunListener只有一個實現類:EventPublishingRunListener,在這個實現類中,有一個SimpleApplicationEventMulticaster型別的屬性initialMulticaster,所有的事件都是通過這個屬性的multicastEvent方法廣播出去的。