springboot源碼分析(一)

SpringApplication類通過run方法開始進行分析

configureHeadlessProperty();
java.awt.headless默認爲true,用來配置不存在顯示器等系統配置的時候進行使用的配置,類似於服務器。

繼續往下看看到了對應的classLoader相關的代碼

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
}

說到classLoader,順便講一下classLoader
1.負責將class加載到JVM中
2.ClassLoader 通過雙親委任的方式進行加載類
解釋一下雙親加載機制

一個類加載器查找class和resource時,是通過“委託模式”進行的,它首先判斷這個class是不是已經加載成功,如果沒有的話它並不是自己進行查找,而是先通過父加載器,然後遞歸下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果沒有找到,則一級一級返回,最後到達自身去查找這些對象。這種機制就叫做雙親委託。
custom <-> AppclassLoader <-> extenstionClassLoader <-> bootstrap classLoader(c++ c)
爲什麼採用雙親委任機制:
因爲這樣可以避免重複加載,當父親已經加載了該類的時候,就沒有必要子ClassLoader再加載一次。從安全角度考慮,如果不使用這種委託模式,我們自己定義一個Map來動態替代java核心api中定義的類型,這樣會存在非常大的安全隱患,而雙親委託的方式,就可以避免這種情況,因爲Map在啓動時就被引導類加載器(Bootstrcp ClassLoader)加載,所以用戶自定義的ClassLoader永遠也無法加載一個自己寫的Map,除非你改變JDK中ClassLoader搜索類的默認算法。

Bootstrap ClassLoader:javaPath/libextensionClassLoader:javaPath/lib extension ClassLoader :javaPath/lib/ext
appClass loader:classpath 所有的類

3.顯示加載和隱式加載。
顯示加載是通過this.getClass | Class.forName | this.getClass.getClassLoader
隱式加載主要是以下幾種方式
1)創建類的實例
2)訪問某個類/接口的靜態變量,或者賦值
3)調用類的靜態方法
4)初始化類的子類
5)java虛擬機啓動標註啓動類的類,比如啓動指定 main函數的類

4.源碼分析開始

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService = true;
        this.headless = true;
        this.registerShutdownHook = true;
        this.additionalProfiles = new HashSet();
        this.isCustomEnvironment = false;
        this.lazyInitialization = false;
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

在使用springboot的時候,我們是使用 SpringApplication.run(Application.class,args); 開始啓動服務,在實例化SpringApplication的時候開始注入相關數據,其中有一項是 this.webApplicationType,打開之後會發現如下代碼

    /**
	 * The application should not run as a web application and should not start an
	 * embedded web server.
	 */
	NONE,

	/**
	 * The application should run as a servlet-based web application and should start an
	 * embedded servlet web server.
	 */
	SERVLET,

	/**
	 * The application should run as a reactive web application and should start an
	 * embedded reactive web server.deduceWebEnvironment
	 */
	REACTIVE;

除了我們已知的傳統的SERVLET的WEB容器,還採用的新型的REACTIVE響應式編程,就是springboot2.x加入了webFlux,webFlux是一個異步非阻塞式的 Web 框架,它能夠充分利用多核 CPU 的硬件資源去處理大量的併發請求。
WebFlux 內部使用的是響應式編程(Reactive Programming),以 Reactor 庫爲基礎, 基於異步和事件驅動,可以讓我們在不擴充硬件資源的前提下,提升系統的吞吐量和伸縮性。

我們開始分析springboot啓動過程中所有的操作
1.所有監聽開始運行

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

看到以上代碼就應該清除,springboot所有監聽器會在這裏進行啓動,關於springboot源碼監聽器有如下幾種

name introduction
ClearCachesApplicationListener 應用上下文加載完成後對緩存做清除工作,響應事件ContextRefreshedEvent
ParentContextCloserApplicationListener 監聽雙親應用上下文的關閉事件並往自己的孩子應用上下文中傳播,相關事件ParentContextAvailableEvent/ContextClosedEvent
ClearCachesApplicationListener 應用上下文加載完成後對緩存做清除工作,響應事件ContextRefreshedEvent
FileEncodingApplicationListener 如果系統文件編碼和環境變量中指定的不同則終止應用啓動。具體的方法是比較系統屬性file.encoding和環境變量spring.mandatory-file-encoding是否相等(大小寫不敏感)
AnsiOutputApplicationListener 根據spring.output.ansi.enabled參數配置AnsiOutput
ConfigFileApplicationListener EnvironmentPostProcessor,從常見的那些約定的位置讀取配置文件,比如從以下目錄讀取application.properties,application.yml等配置文件:classpath: file:.classpath:config file:./config/:也可以配置成從其他指定的位置讀取配置文件
DelegatingApplicationListener 監聽到事件後轉發給環境變量context.listener.classes指定的那些事件監聽器
ClasspathLoggingApplicationListener 一個SmartApplicationListener,對環境就緒事件ApplicationEnvironmentPreparedEvent/應用失敗事件ApplicationFailedEvent做出響應,往日誌DEBUG級別輸出TCCL(thread context class loader)的classpath。
LoggingApplicationListener 配置LoggingSystem。使用logging.config環境變量指定的配置或者缺省配置
LiquibaseServiceLocatorApplicationListener 使用一個可以和Spring Boot可執行jar包配合工作的版本替換liquibase ServiceLocator
BackgroundPreinitializer 儘早觸發一些耗時的初始化任務,使用一個後臺線程

以上是springboot定義的一些listener。每個listener作用各不相同對應的event對應不同生命週期。

關於lisntener,可以看到一個類 EventPublishingRunListener,相關代碼如下

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;

	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster
				.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
	}

	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster
				.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
	}

	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
	}

	@Override
	public void started(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
	}

	@Override
	public void running(ConfigurableApplicationContext context) {
		context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
	}

	@Override
	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
		if (context != null && context.isActive()) {
			// Listeners have been registered to the application context so we should
			// use it at this point if we can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			if (context instanceof AbstractApplicationContext) {
				for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
						.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			this.initialMulticaster.multicastEvent(event);
		}
	}

	private static class LoggingErrorHandler implements ErrorHandler {

		private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);

		@Override
		public void handleError(Throwable throwable) {
			logger.warn("Error calling ApplicationEventListener", throwable);
		}

	}

對應類 EventPublishingRunListener 中的方法,有不同的執行週期,週期如下表

name introduction
ApplicationStartingEvent 在Environment和ApplicationContext可用之前 & 在ApplicationListener註冊之後發佈。
ApplicationEnvironmentPreparedEvent 在context中 Environment被使用的時候並在context被創建之前
ApplicationContextInitializedEvent 在bean定義加載之前 & ApplicationContextInitializers被調用之後 & ApplicationContext開始準備之後發佈
ApplicationPreparedEvent 在ApplicationContext完全準備好並且沒有刷新之前發佈,此時bean定義即將加載,Environment已經準備好被使用。
ApplicationStartedEvent 在ApplicationContext刷新之後,調用ApplicationRunner和CommandLineRunner之前發佈
ApplicationReadyEvent 應用已經準備好,可以接收請求
ApplicationFailedEvent 在啓動過程中如果發生異常則觸發。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章