使用的SpringBoot版本為2.3.12,配套Spring版本為5.2.15
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業的熱愛。我們立志把好的技術通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:域名注冊、網絡空間、營銷軟件、網站建設、內鄉網站維護、網站推廣。一 SpringBoot啟動流程 1.程序入口// 程序入口
// 運行SpringApplication.run方法
// 主啟動類方法
// args是啟動參數,我在環境配置處配置程序參數了--server.port=8079端口號信息,則args不為空
public static void main(String[] args) {
// 加載完成后 執行run方法
SpringApplication.run(SpringbootQuartzApplication.class, args);
}
// 上面代碼實際上是調用spring-boot包里的SpringApplication.class里的方法
// 這里可以看出是先創建SpringApplication對象,再調用run方法完成容器的初始化操作
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
2.主體分為兩個步驟a.構造方法創建SpringApplication實例
b.執行SpringApplication實例的run方法
3.圖示 二 new SpringAoolication()構造函數隨版本不同,代碼會有較大差異,具體根據自己所用項目來。下面截取構造方法核心部分代碼。
// new SpringApplication()構造函數
public static ConfigurableApplicationContext run(Class>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
其中new SpringApplication(primarySources)構造函數環節調用的也是SpringApplication.class里的方法
public SpringApplication(Class>... primarySources) {
this((ResourceLoader)null, primarySources);
}
// 下面會逐一展開run方法執行時,是如何使用構造方法里定義的屬性的
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
// 1.初始化啟動時必要參數
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
// 打印JVM的啟動和運行信息,啟動類、java版本等
this.logStartupInfo = true;
// 通過命令行參數向application.properties中添加屬性配置
this.addCommandLineProperties = true;
// 加載默認的類型轉換和格式化類
this.addConversionService = true;
// 開啟java的headless模式,此模式允許java在沒有顯示器、鼠標等設備的情況下啟動服務
this.headless = true;
// 創建線程,該線程用來在java程序關閉后釋放資源
this.registerShutdownHook = true;
// 默認為空,即使用springboot默認配置文件。開發時切dev/sit/prod等分支時會使用
this.additionalProfiles = new HashSet();
// 是否加載環境配置
this.isCustomEnvironment = false;
// 是否懶加載,可以通過在application.properties設置spring.main.lazy-initialization=true修改
this.lazyInitialization = false;
//
this.resourceLoader = resourceLoader;
// 主配置為空則拋出異常
Assert.notNull(primarySources, "PrimarySources must not be null");
// 保存主配置類
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 2 確認web應用類型,本項目為Servlet
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 3.加載所有初始化器,獲取spring工廠文件中的實例。加載了當前項目下全部的spring.factries文件,將文件中的k-v放入緩存中,然后將讀取到的ApplicationContextInitializer類型的類實例化
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 4.加載所有監聽器,這里與上面類似也調用this.getSpringFactoriesInstances(),但是由于之前已經把spring.factries文件讀取到緩存中了,因此不用再次讀取文件而是讀緩存
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 5.設置程序運行的主類MainApplication,以便后面執行run方法
this.mainApplicationClass = this.deduceMainApplicationClass();
}
2.確定應用程序類型// 提供三種類型
// 1.NONE 表示當前的應用既不是一個web應用也不是一個reactive應用,是一個純后臺的應用。不啟動內嵌的服務
// 2.SERVLET 當前應用是一個基于servlet API的標準的web應用,需啟動內嵌servlet web服務
// 3.REACTIVE 是spring5當中的新特性,表示是一個響應式的web應用,需啟動內嵌的響應式web服務
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 這里實際上是根據類加載器名字具體是哪一種,本項目中SERVLET_INDICATOR_CLASSES為['javax.servlet.Servlet', 'org.springframework.web.context.ConfigurableWebApplicationContext']
// ClassUtils.isPresent()方法,判斷指定類名的類是否存在,是否可以進行加載(如果我們導入了,相對應的jar包,就可以加載,如果沒有導入就不能加載)
// 如果能夠加載DispatcherHandler類,但是無法加載DispatcherServlet和ServletContainer類,就證明是一個響應式web應用
static WebApplicationType deduceFromClasspath() {
// 判斷能否加載響應式web應用
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2< var1; ++var2) {
String className = var0[var2];
// 判斷能否加載SERVLET_INDICATOR_CLASSES里的兩個類名
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
// 補充ClassUtils.isPresent()方法 實際上是調用forName來獲取類加載器
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
} catch (IllegalAccessError var3) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + var3.getMessage(), var3);
} catch (Throwable var4) {
return false;
}
}
3.加載所有初始化器,獲取spring工廠文件中的實例補充說明,4.加載監聽器與下面流程基本一致,不同的是3.加載所有初始化器時讀取的一些文件配置會放入緩存中,加載監聽器時從緩存讀取數據即可。
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
privateCollectiongetSpringFactoriesInstances(Classtype) {
// type為ApplicationContextInitializer.class
return this.getSpringFactoriesInstances(type, new Class[0]);
}
privateCollectiongetSpringFactoriesInstances(Classtype, Class>[] parameterTypes, Object... args) {
// 獲取SpringApplication的類加載器,這里是一個系統類加載器AppClassLoader
ClassLoader classLoader = this.getClassLoader();
// 加載spring.factories文件中的類的全類名集合
Setnames = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 通過反射獲取到集合中全類名對應的Class,并創建其實例對象,返回實例對象
Listinstances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 根據優先級排序
AnnotationAwareOrderComparator.sort(instances);
// 返回實例
return instances;
}
public static ListloadFactoryNames(Class>factoryType, @Nullable ClassLoader classLoader) {
// 加載初始化器時factoryTypeName="org.springframework.context.ApplicationContextInitializer",對應之前設置的ApplicationContextInitializer.class
// 加載監聽器時factoryTypeName="org.springframework.context.ApplicationListener",對應ApplicationListener.calss
String factoryTypeName = factoryType.getName();
// 正式加載spring-boot包里META-INF目錄下的spring.factories文件
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
// loadSpringFactories()才正式去加載spring-boot包里META-INF目錄下的spring.factories文件
private static Map>loadSpringFactories(@Nullable ClassLoader classLoader) {
// 這里是讀緩存,加載初始化器時還沒有緩存(在這一環節添加了緩存),到加載監聽器時就能從緩存讀取數據了
MultiValueMapresult = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
// 獲取當前項目中所有META-INF/spring.factories文件的絕對路徑,對于目前使用的項目則是讀取下面的路徑,因為是測試項目,因此引入的包和版本比較雜亂
// file:/E:/javaDevelopmentTools/maven/repository/org/springframework/boot/spring-boot/2.3.12.RELEASE/spring-boot-2.3.12.RELEASE.jar!/META-INF/spring.factories
// file:/E:/javaDevelopmentTools/maven/repository/org/springframework/spring-beans/5.2.15.RELEASE/spring-beans-5.2.15.RELEASE.jar!/META-INF/spring.factories
// file:/E:/javaDevelopmentTools/maven/repository/org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/2.0.1/mybatis-spring-boot-autoconfigure-2.0.1.jar!/META-INF/spring.factories
// file:/E:/javaDevelopmentTools/maven/repository/com/baomidou/mybatis-plus-boot-starter/3.5.1/mybatis-plus-boot-starter-3.5.1.jar!/META-INF/spring.factories
// file:/E:/javaDevelopmentTools/maven/repository/org/springframework/boot/spring-boot-autoconfigure/2.3.12.RELEASE/spring-boot-autoconfigure-2.3.12.RELEASE.jar!/META-INF/spring.factories
// 這里貼一下pom.xml文件的部分引用Enumerationurls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
// 遍歷上面獲取到的絕對路徑
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
// 把文件資源加載到Properties中
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry, ?>entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11< var10; ++var11) {
String factoryImplementationName = var9[var11];
// K-V形式,把spring.factories中的的類型,下面圖例中會貼上result的內容
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
// 將類加載器加載的spring.factories文件結果集放入緩存
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
并且貼一下對應圖片,下圖為result.add(factoryTypeName, factoryImplementationName.trim());后的result。大多是與項目啟動、異常處理、自動配置等基礎配置相關的內容。
下面則是讀取了哪些spring.factories文件(實際項目中會很多,這里只是測試項目)
三 執行run方法下面貼一下主要代碼部分
public ConfigurableApplicationContext run(String... args) {
// 開啟計時器。計算程序啟動時間用的
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
// 6.開啟java的headless模式 是SpringApplication的 this.headless = true;控制的
this.headless = true;
// 保證headless一定有值
this.configureHeadlessProperty();
// 7.獲取并啟用監聽器 這里獲取到的是SpringApplicationRunListeners對象,里面有之前初始化SpringApplication時的監聽器
// args默認為空,我填寫了--server.port=8079屬性
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
try {
// 8.設置應用程序參數
// 初始化ApplicationArguments對象,提供訪問運行SpringApplication時的參數
// 由于之前的配置 args 里帶有--server.port=8079參數,而這個參數就會被封裝進Source對象,底層是Spring 框架的 SimpleCommandLinePropertySource
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 9.準備環境變量 參數是監聽器和應用程序參數
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
// 忽略掉部分bean信息
this.configureIgnoreBeanInfo(environment);
// 打印banner信息 這里實際上是打印了Spring的標志
Banner printedBanner = this.printBanner(environment);
// 10.創建應用程序的上下文
context = this.createApplicationContext();
// 10.準備上下文環境
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 11.刷新上下文,屬于Spring的范疇(自動裝配+啟動tomcat)
this.refreshContext(context);
// 11.刷新上下文后置處理(目前是空的)
this.afterRefresh(context, applicationArguments);
// 停止計時器
stopWatch.stop();
// 打印JVM的啟動和運行信息,啟動類、java版本等 this.logStartupInfo = true;
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 創建應用上下文啟動完成事件對象ApplicationStartedEvent
// 發布應用上下文啟動完成事件,廣播事件,調用監聽器相對應的監聽方法
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var9) {
this.handleRunFailure(context, var9, listeners);
throw new IllegalStateException(var9);
}
try {
// 12.發布上下文準備就緒事件
listeners.running(context);
return context;
} catch (Throwable var8) {
this.handleRunFailure(context, var8, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var8);
}
}
7.獲取并啟用監聽器
9.準備環境變量
9.1ConfigurableEnvironmentprivate ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// 創建或者返回環境
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
// 配置環境
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach((Environment)environment);
// 監聽器的環境準備,這部分.class文件斷點匹配不上,但是從輸出上看多了initialMulticaster
listeners.environmentPrepared((ConfigurableEnvironment)environment);
// 把環境綁定到SpringApplication
this.bindToSpringApplication((ConfigurableEnvironment)environment);
// 之前配置的 this.isCustomEnvironment = false;
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
// 返回的environment環境信息不僅僅是SpringBoot項目本身的,虛擬機、項目運行系統信息(Linux/Windows等)、JDK、MAVEN等信息也包含其中
return (ConfigurableEnvironment)environment;
}
// 由于沒有設置environment,因此根據this.webApplicationType = WebApplicationType.deduceFromClasspath();得知為SERVLET
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
} else {
switch(this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
}
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
// 之前設置的this.addConversionService = true;加載默認的類型轉換和格式化類
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService)conversionService);
}
// 配置environment的PropertySources
this.configurePropertySources(environment, args);
// 配置environment的ActiveProfiles
this.configureProfiles(environment, args);
}
9.2忽略Bean的BeanInfo信息// spring.beaninfo.ignore默認為true表示跳過對BeanInfo類的搜索
// 忽略所有自定義的BeanInfo信息的搜索
// 通過Java的Introspector.getBeanInfo方法,可以獲取到JavaBean的BeanInfo信息(一般有Bean的方法、屬性、類相關信息等)
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty("spring.beaninfo.ignore") == null) {
Boolean ignore = (Boolean)environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
System.setProperty("spring.beaninfo.ignore", ignore.toString());
}
}
9.3打印banner信息這里實際上是打印了Spring的標志
private Banner printBanner(ConfigurableEnvironment environment) {
// 之前設置的this.bannerMode = Mode.CONSOLE;
if (this.bannerMode == Mode.OFF) {
return null;
} else {
ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader((ClassLoader)null);
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter((ResourceLoader)resourceLoader, this.banner);
return this.bannerMode == Mode.LOG ? bannerPrinter.print(environment, this.mainApplicationClass, logger) : bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
}
console控制臺會打印出下面標識(默認輸出,可以通過配置修改)
10.創建應用程序的上下文 10.1ConfigurableApplicationContextprotected ConfigurableApplicationContext createApplicationContext() {
Class>contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根據Web應用類型創建對應的上下文,目前項目這里根據之前的配置 是SERVLET
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
// 實例化應用上下文
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
10.2準備上下文環境private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 把之前的配置的環境加載到應用上下文中
context.setEnvironment(environment);
// 實例化單例的beanName生成器
this.postProcessApplicationContext(context);
// 遍歷之前在SpringApplication中的初始化器,進行初始化
this.applyInitializers(context);
// 監聽器初始化
listeners.contextPrepared(context);
// 打印JVM的啟動和運行信息,啟動類、java版本等 this.logStartupInfo = true;
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 將啟動參數注冊到容器中
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
Set
11.刷新上下文(這部分會調用Spring里的代碼,包括自動裝配Tomcat)private void refreshContext(ConfigurableApplicationContext context) {
// 之前設置的this.registerShutdownHook = true;創建線程,該線程用來在java程序關閉后釋放資源
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
this.refresh((ApplicationContext)context);
}
上面的this.refresh((ApplicationContext)context);其實就在調用Spring相關包了
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// 刷新前準備工作,設置了容器的啟動時間、加載environment對象、準備監聽器等
this.prepareRefresh();
// 獲取BeanFactory,類型是DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// 把配置和屬性信息填充到beanFactory中
this.prepareBeanFactory(beanFactory);
try {
//
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
// 注冊BeanPostProcessor(初始化時才會調用)
this.registerBeanPostProcessors(beanFactory);
// 初始化MessageSource組件,消息處理相關
this.initMessageSource();
// 事件廣播相關
this.initApplicationEventMulticaster();
// 具體由子類實現,根據Spring容器的種類進行不同的事情處理.
this.onRefresh();
// 注冊引用的監聽器
this.registerListeners();
// 實例非懶加載的單例
this.finishBeanFactoryInitialization(beanFactory);
// 完成刷新并進行通知廣播
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
11.1beanFactoryConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();這部分
11.2this.onRefresh();根據不同Spring容器來的創建不同容器
// 項目時web項目,用的ServletWebServerApplicationContext
// 對應代碼如下,由this.createWebServer();創建具體的Servlet容器
protected void onRefresh() {
super.onRefresh();
try {
// 由于SpringBoot內置Tomcat容器,所以這里實際上會創建一個TomcatServletWebServerFactory生成TomcatWebServer
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
當前標題:SpringBoot啟動流程-創新互聯
URL分享:http://vcdvsql.cn/article6/csepig.html
成都網站建設公司_創新互聯,為您提供網站排名、網站營銷、搜索引擎優化、建站公司、網站設計、網站設計公司
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯