1. 程式人生 > >Spring Boot 原始碼分析

Spring Boot 原始碼分析

Spring Boot是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。通過這種方式,Boot致力於在蓬勃發展的快速應用開發領域(rapid application development)成為領導者。

Spring boot的特點

  1. 建立獨立的Spring應用程式
  2. 嵌入的Tomcat,無需部署WAR檔案
  3. 簡化Maven配置
  4. 自動配置Spring
  5. 提供生產就緒型功能,如指標,健康檢查和外部配置
  6. 絕對沒有程式碼生成和對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的初始化大致分為以下的步驟:

  1. 判斷是否是web應用程式
  2. 從所有類中查詢META-INF/spring.factories檔案,載入其中的初始化類和監聽類。
  3. 查詢執行的主類 預設初始化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過程

  1. 註冊一個StopWatch,用於監控啟動過程
  2. 獲取監聽器SpringApplicationRunListener,用於springboot啟動過程中的事件廣播
  3. 設定環境變數environment
  4. 建立spring容器
  5. 建立FailureAnalyzers錯誤分析器,用於處理記錄啟動過程中的錯誤資訊
  6. 呼叫所有初始化類的initialize方法
  7. 初始化spring容器
  8. 執行ApplicationRunner和CommandLineRunner的實現類
  9. 啟動完成
	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方法廣播出去的。