天天看點

SpringBoot源碼解析系列(1) 啟動源碼分析

序章 : 這是我拔了将近4個月SpringBoot源碼 , 本人本二  剛開始自學的時候有幸接觸到第一本書籍是java程式設計思想 故對java程式設計産生了濃厚的興趣 是以想着人人為我 我為人人 故出此專題  雖然出此專題 但我想說的是還是建議大家多看看Spring相關源碼 對于代碼感覺 , 對于設計模式以及抽象事務了解即為提高 !! 希望大家能也去扒扒源碼

1 . SpringBoot概述

Build Anything with Spring Boot:Spring Boot is the starting point for building all Spring-based applications. Spring Boot is designed to get you up and running as quickly as possible, with minimal upfront configuration of Spring.(引用官網)

  • 它使用 “習慣優于配置” (基本上根據使用者習慣配置了很多預設配置 同時還支援修改)的理念讓你的項目快速運作起來。
  • 它并不是什麼新的架構,而是預設配置了很多架構的使用方式,就像 Maven 整合了所有的 jar 包一樣,Spring Boot 整合了所有架構 (通過xxx-starter包關聯起來)

Spring源碼中常用的單詞總結 : (本人英語不是很好 開始讀spring相關源碼時極其難受 後來我會針對一些高頻英文單詞做一下筆記 我先把這個英文單詞貼出來 供大家參考  後來基本上達到的水準是給我一個類名  結合着接口 我大概能猜出來這個類是幹什麼用的 以及有哪些重要方法)

handler處理程式
adapter擴充卡
resolver解析器(解決器)
registy注冊器
mapping映射
Definition定義
Expression表達式
Converter轉換器
Support支援
Profile個人資料(配置檔案什麼的)
Scope作用域
Alias别名
Publisher發行
Configurable可配置的
Required需要
Instantiation執行個體化
refresh重新整理
Boot開機
Multicaster廣播
Standard标準 (Spring中基本上如果你不是預設的話 都是走的是這個)
validate驗證
Arguments争論
bootstrap引導程式
PostProcessor 後置處理器
Generic通用
Metadata中繼資料
skip跳過
priority優先
internal内部
Evaluator評估
Parser解析器
candidate候選
excluded限制
ignore忽略
validate證明
Customizer定制器
wrapper包裝
           

介于目前好多部落格都有啟動方式 也非常簡單 我這裡就不細說如何啟動 我們直接進入正題 看看 SpringBoot是如何實作了那麼多流弊功能

@SpringBootApplication()
public class InsAcaConsoleMain {
    public static void main(String[] args) {
        SpringApplication.run(InsAcaConsoleMain.class, args);
    }
}
           

這是一個簡單到不能再簡單地入口類  主要是通過 @SpringBootApplication()這個注解實作了自動裝載 這個自動裝載實作過程我會單獨開一個話題講解

接下來 走到了建立了一個SpringApplication 構造器函數如下 : 

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	//資源裝載
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//主加載類 就是本文的InsAcaConsoleMain
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//判斷 到底是 WebApplicationType.SERVLET 還是什麼 如果不進行設定的話 預設是Serverlet !!!! 詞條後面關聯建立不同容器以及環境 甚至跟ServerletContext都有關 很關鍵
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 從MATE.INF下拿到所有key為 ApplicationContextInitializer !!!!并指派
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		// 從MATE.INF下拿到所有key為 ApplicationListener !!!!并指派
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
           

SpringBoot能通過SpringFactoriesLoader.loadFactoryNames(type, classLoader)); type為接口類型 拿到所有依賴的jar包下/MATA-INF下的spring.factories裡面的配置 裡面是key - value 一般key為接口名稱 這樣就實作了動态加載一些類 底層調用的是classLoader.getResources jdk源碼關于這部分寫的挺有意思 感興趣可以扒一下 在此子產品我就不先講jdk了

//啟動監控表
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//模拟系統spring為沒有鍵盤滑鼠等狀态
configureHeadlessProperty();
//傳回SpringApplicationRunListener 從spring.factories拿到傳回SpringApplicationRunListener 
//此類是跟SpringBoot事件有關 來會根據加載情況釋出不同的事件 對于監聽該事件的會進行跑監聽事件 
//且SpringBoot隻有一個實作類EventPublishingRunListener
//如果想魔改此類 新增事件釋出 就也需要配置新的SpringApplicationRunListener 釋出新的事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
	// 把傳入的args進行解析 生成key - value 後面會将這個類注冊成bean
	ApplicationArguments applicationArguments = new DefaultApplicationArguments(
			args);
    //詳細說
	ConfigurableEnvironment environment = prepareEnvironment(listeners,
			applicationArguments);
	configureIgnoreBeanInfo(environment);
	//列印banner 就是那個SpringBoot圖示 後面也會将這個對象注冊成bean
	Banner printedBanner = printBanner(environment);
	// 會建立 AnnotationConfigApplicationContext容器
	context = createApplicationContext();
	//獲得異常輸出exceptionReporters (Spring.factories裡面拿的 可以自定義)
	exceptionReporters = getSpringFactoriesInstances(
			SpringBootExceptionReporter.class,
			new Class[] { ConfigurableApplicationContext.class }, context);
	// 準備環境
	prepareContext(context, environment, listeners, applicationArguments,
			printedBanner);
	// 這個類很重要 涉及到Spring核心refresh功能 需要 單獨開專題講解
	refreshContext(context);
	// null方法 如果想改架構 想實作什麼功能 覆寫掉這個方法
	afterRefresh(context, applicationArguments);
	stopWatch.stop();
	if (this.logStartupInfo) {
		new StartupInfoLogger(this.mainApplicationClass)
				.logStarted(getApplicationLog(), stopWatch);
	}
	//釋出ApplicationStartedEvent事件
	listeners.started(context);
	// 從容器裡面拿到ApplicationRunner跟CommandLineRunner實作類 跑對應的run方法
	callRunners(context, applicationArguments);
}
catch (Throwable ex) {
	handleRunFailure(context, ex, exceptionReporters, listeners);
	throw new IllegalStateException(ex);
}

try {
    //釋出ApplicationReadyEvent事件
	listeners.running(context);
}
catch (Throwable ex) {
	handleRunFailure(context, ex, exceptionReporters, null);
	throw new IllegalStateException(ex);
}
return context;
           

上面代碼邏輯就是SpringApplication#run方法的簡單注釋講解        下面我會針對重要方法進行深度講解

第一個重要方法 SpringApplication # prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		//建立StandardEnvironment環境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//重要
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//釋出ApplicationEnvironmentPreparedEvent事件
		listeners.environmentPrepared(environment);
		//将目前環境綁定到目前SpringApplication
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		//往環境裡面添加key為configurationProperties的PropertySources
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
           

第二個重要方法  configureEnvironment

protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
		if (this.addConversionService) {
		// 把Spring預設的Converter添加進環境 他是将一個類型轉換成另外的類型 比如date轉換成localdatetime
		// 同時也支援可擴充
			ConversionService conversionService = ApplicationConversionService
					.getSharedInstance();
			environment.setConversionService(
					(ConfigurableConversionService) conversionService);
		}
		// 添加比如java預設參數 或者環境變量啥的 
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	}
           

其實到這裡 SpringBoot基本啟動類就已經結束了 但Spring的魅力遠遠不止如此   接下來我會對SpringBoot以及Spring中一些常見的實作進行詳細講解 !!!!!!!!!!!!!!!!!!!!!!!!!!

下一節SpringBoot源碼解析系列(2) SpringBoot如何掃描并将對象注冊Beandefinition : https://blog.csdn.net/weixin_44669461/article/details/115910478?spm=1001.2014.3001.5501

繼續閱讀