學透Spring Boot — 014. Spring MVC的自動配置

這是學透Spring Boot的第14篇文章,更多文章請移步我的專欄:

學透 Spring Boot_postnull咖啡的博客-CSDN博客

目錄

沒有Spring Boot時的Spring MVC

使用Spring Boot后的Spring MVC

Spring MVC的自動配置解析

明確目標

入口類

Spring容器的啟動

Spring容器生命周期hook類

配置類解析類 ConfigurationClassParser

自動配置類列表 AutoConfiguration.imports

Spring MVC的自動配置類WebMvcAutoConfiguration

自動配置的視圖解析器?

Http消息轉換器自動配置類

數據源自動配置類?DataSourceAutoConfiguration

RestClient的自動配置?RestClientAutoConfiguration

嵌入式的web容器配置?

DispatcherServlet的自動配置

總結


沒有Spring Boot時的Spring MVC

早些年還沒有Spring Boot的時候,我們開發一個Spring MVC應用,需要做一大堆的配置,而且和其它的項目比較,這些配置大部分都是大同小異的,我們也可以稱之為樣板配置。

所以每次新建一個項目,我們通常是復制一個項目,然后復制這個項目的配置,做少量的修改,雖然沒有什么太大的問題,但是如果一不小心改錯,可能半天都找不到問題。

可以參考之前的一篇文章,里面介紹了沒有Spring Boot時完整的手動配置。

學透Spring Boot — [二] Spring 和 Spring Boot的比較-CSDN博客

我們可以大概看看傳統Spring MVC項目的配置

web.xml 中配置DispatchServlet

在 servlet-context.xml 中配置 Spring MVC 相關組件

這兩份配置,非常冗余,因為絕大部分項目都大同小異。

最后再實現控制器

使用Spring Boot后的Spring MVC

如果使用Spring Boot,事情變得非常簡單。

我們只要在我們的應用啟動類添加一個注解?@SpringBootApplication

然后,所有的事情,SpringBoot都會自動幫我們完成。

簡直是單車 到 摩托車的飛躍。

Spring MVC的自動配置解析

下面我們一步步來研究,Spring Boot是如何做到自動配置MVC的。

明確目標

自動配置的結果,就是把手動顯示的配置,變成自動的配置。

比如servlet-context.xml中配置的視圖解析器

Spring Boot 它會通過@Bean聲明的方式,幫我們創建一個視圖解析器的Bean

我們今天的任務,就是要搞清楚,Spring Boot在哪里以及什么時候,幫我創建的這個Bean。

入口類

首先我們的入口類,使用了一個注解@SpringBootApplication。

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

它其實是個組合注解。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {

我們重點關注@EnableAutoConfiguration 這個注解,它是一個總開關,開啟了自動配置的新世界。

這個注解也是一個組合注解。

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

通過@Import(AutoConfigurationImportSelector.class)這個注解,會觸發自動配置類的導入,spring boot會用這個類去完成自動配置的功能。

Spring容器的啟動

這次,我們先看看Spring Boot的啟動,來分析自動配置是如何生效。

public class SpringApplication {public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}
}

SpringBoot的run方法,會創建并刷新Spring容器。

public ConfigurableApplicationContext run(String... args) {context = createApplicationContext();context.setApplicationStartup(this.applicationStartup);prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);refreshContext(context); // 刷新 Spring 容器,加載各種 BeanafterRefresh(context, applicationArguments);startup.started();
}

關注:refreshContext(context); // 刷新 Spring 容器,加載各種 Bean

接著刷新容器

刷新容器的關鍵過程包括 Bean 的加載與初始化。refreshContext 方法會啟動各類 Bean 的生命周期,調用 invokeBeanFactoryPostProcessors 來執行 BeanFactory 后處理器。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());if (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean("loadTimeWeaver")) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}
}

重點關注:invokeBeanFactoryPostProcessors

我們理解成Spring提供的生命周期鉤子就行。通過這些鉤子,我們可以在Spring啟動過程中,做一些特殊的工作。比如自動化配置各種bean。

Spring容器生命周期hook類

其中Spring就提供了一個鉤子類。

它是生命周期類,所以啟動過程中自動被找到并執行。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanRegistrationAotProcessor, BeanFactoryInitializationAotProcessor, PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {
}

這個類會去處理配置類 也就是加了@Configuration的類

    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");parser.parse(candidates);parser.validate();}

配置類解析類 ConfigurationClassParser

接下來,它把任務交給了解析器——ConfigurationClassParser

最終這個解析工具類,通過一長串的調用鏈,最終到了另一個工具類ImportCandidates

public final class ImportCandidates implements Iterable<String> {public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {Assert.notNull(annotation, "'annotation' must not be null");ClassLoader classLoaderToUse = decideClassloader(classLoader);String location = String.format(LOCATION, annotation.getName());Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);List<String> importCandidates = new ArrayList<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();importCandidates.addAll(readCandidateConfigurations(url));}return new ImportCandidates(importCandidates);}
}

我們重點看這一行代碼

String location = String.format(LOCATION, annotation.getName());

其中:private static final String LOCATION = "META-INF/spring/%s.imports";

所以location是:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

這個文件,在我們的自動配置模塊下

自動配置類列表 AutoConfiguration.imports

我們把這個文件的內容羅列出來

是不是很多名字都似曾相識呢?

是的,這就是SpringBoot提供的自動配置類,對各種常用的組件,都提供了自動配置類。

SpringBoot在啟動Spring容器的過程中,會定位到這個文件,然后逐個嘗試去加載配置類。

Spring MVC的自動配置類WebMvcAutoConfiguration

我們先重點關注其中一個WebMvcAutoConfiguration

這個類提就是Spring MVC的自動配置類。

這個配置類會被找到,但是要不要加載,得看條件。條件配置就是它上面的注解。

我們逐條解析:

@AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
?? ??? ?ValidationAutoConfiguration.class }

DispatcherServlet配置完后,才會配置Spring MVC。

說得通,就像我們先配置web.xml中的DispatcherServlet,再配置Spring mvc的配置servlet.xml

@ConditionalOnWebApplication(type = Type.SERVLET)

必須是Spring MVC(Servlet)的web才會加載。

如果是WebFlux的web,就不會自動配置MVC。

@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })

Classpath下有這個兩個類。

表示我們引入了Spring mvc的依賴。

@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

只有當Spring容器沒有WebMvcConfigurationSupport這個bean時,才會配置MVC。

因為這個Bean是用來給我們自定義的,如果我們不想用自動配置,而是想覆蓋默認配置,我們就需要繼承這個類。這樣,SpringBoot就以我們配置的為主,而忽略自動配置。

這些條件,我們都滿足,所以Spring Boot開始用這個類進行自動配置MVC。

這個配置類中定義了很多Bean,這些bean就是MVC的組件。

自動配置的視圖解析器?

其中,就包含默認的視圖解析器。

		@Bean@ConditionalOnMissingBeanpublic InternalResourceViewResolver defaultViewResolver() {InternalResourceViewResolver resolver = new InternalResourceViewResolver();resolver.setPrefix(this.mvcProperties.getView().getPrefix());resolver.setSuffix(this.mvcProperties.getView().getSuffix());return resolver;}

還記得我們在傳統Spring MVC項目手動的配置嗎?是的,我們做到了,通過自動創建Bean的方式,成功完成了視圖解析器的配置。

我們在看看其它的自動配置類。

都定義在org.springframework.boot.autoconfigure.AutoConfiguration.imports這個文件下。

Http消息轉換器自動配置類

@AutoConfiguration(after = { GsonAutoConfiguration.class, JacksonAutoConfiguration.class, JsonbAutoConfiguration.class })
@ConditionalOnClass(HttpMessageConverter.class)
@Conditional(NotReactiveWebApplicationCondition.class)
@Import({ JacksonHttpMessageConvertersConfiguration.class, GsonHttpMessageConvertersConfiguration.class,JsonbHttpMessageConvertersConfiguration.class })
@ImportRuntimeHints(HttpMessageConvertersAutoConfigurationRuntimeHints.class)
public class HttpMessageConvertersAutoConfiguration {

它導入了三種解析器

@Import({ JacksonHttpMessageConvertersConfiguration.class,         GsonHttpMessageConvertersConfiguration.class,JsonbHttpMessageConvertersConfiguration.class })

其中Jackson的是

		@Bean@ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class,ignoredType = {"org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter","org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" })MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {return new MappingJackson2HttpMessageConverter(objectMapper);}

數據源自動配置類?DataSourceAutoConfiguration

	@Configuration(proxyBeanMethods = false)@Conditional(PooledDataSourceCondition.class)@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })protected static class PooledDataSourceConfiguration {@Bean@ConditionalOnMissingBean(JdbcConnectionDetails.class)PropertiesJdbcConnectionDetails jdbcConnectionDetails(DataSourceProperties properties) {return new PropertiesJdbcConnectionDetails(properties);}}

RestClient的自動配置?RestClientAutoConfiguration

	@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)@ConditionalOnMissingBeanRestClient.Builder restClientBuilder(RestClientBuilderConfigurer restClientBuilderConfigurer) {return restClientBuilderConfigurer.configure(RestClient.builder());}

嵌入式的web容器配置?

@AutoConfiguration
@ConditionalOnNotWarDeployment
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {/*** Nested configuration if Tomcat is being used.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })public static class TomcatWebServerFactoryCustomizerConfiguration {@Beanpublic TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,ServerProperties serverProperties) {return new TomcatWebServerFactoryCustomizer(environment, serverProperties);}@Bean@ConditionalOnThreading(Threading.VIRTUAL)TomcatVirtualThreadsWebServerFactoryCustomizer tomcatVirtualThreadsProtocolHandlerCustomizer() {return new TomcatVirtualThreadsWebServerFactoryCustomizer();}}

DispatcherServlet的自動配置

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {DispatcherServlet dispatcherServlet = new DispatcherServlet();dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());return dispatcherServlet;}

總結

我們這篇文章,從另一個角度——Spring容器的啟動過程,結合SpringBoot提供的注解,理解了Spring Boot的自動配置原理。

最終定義到自動配置類的列表文件:

org.springframework.boot.autoconfigure.AutoConfiguration.imports

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/900401.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/900401.shtml
英文地址,請注明出處:http://en.pswp.cn/news/900401.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SQL語句(三)—— DQL

目錄 基本語法 一、基礎查詢 1、查詢多個字段 2、字段設置別名 3、去除重復記錄 4、示例代碼 二、條件查詢 1、語法 2、條件列表常用的運算符 3、示例代碼 三、分組查詢 &#xff08;一&#xff09;聚合函數 1、介紹 2、常見的聚合函數 3、語法 4、示例代碼 &…

LENOVO聯想ThinkBook 16 G6 ABP(21KK)恢復預裝OEM原廠Win11系統鏡像

適用機型&#xff1a;【21KK】 鏈接&#xff1a;https://pan.baidu.com/s/1lbvIh4KTbqm8EZQZfxvNIQ?pwd7vp0 提取碼&#xff1a;7vp0 聯想原裝系統自帶所有驅動、出廠主題壁紙、系統屬性聯機支持標志、Office辦公軟件、聯想瀏覽器、聯想電腦管家、聯想軟件商店、聯想智能引…

# 基于人臉關鍵點的多表情實時檢測系統

基于人臉關鍵點的多表情實時檢測系統 在計算機視覺領域&#xff0c;人臉表情識別技術已經取得了顯著的進展。它不僅可以用于娛樂應用&#xff08;如動態表情包生成&#xff09;&#xff0c;還能在心理健康監測、智能安防、人機交互等領域發揮重要作用。今天&#xff0c;我將分…

在 Ubuntu24.04 LTS 上 Docker Compose 部署基于 Dify 重構二開的開源項目 Dify-Plus

一、安裝環境信息說明 硬件資源&#xff08;GB 和 GiB 的主要區別在于它們的換算基數不同&#xff0c;GB 使用十進制&#xff0c;GiB 使用二進制&#xff0c;導致相同數值下 GiB 表示的容量略大于 GB&#xff1b;換算關系&#xff1a;1 GiB ≈ 1.07374 GB &#xff1b;1 GB ≈ …

SQL Server存儲過程和觸發器的使用

存儲過程 &#xff08;1&#xff09;創建存儲過程&#xff0c;使用Employees表中的員工人數來初始化一個局部變量&#xff0c;并調用這個存儲過程。 1. Create PROCEDURE test number1 int output --輸出參數&#xff0c;可以從程序中返回信息 2. As 3. begin 4. D…

子類是否能繼承

繼承 父類&#xff1a; 子 類 構造方法 非私有 不能繼承 私有&#xff08;private&#xff09;不能繼承 成員變量 非私有 能繼承 私有&…

2025年【山東省安全員C證】考試題及山東省安全員C證考試內容

在當今建筑行業蓬勃發展的背景下&#xff0c;安全生產已成為企業生存與發展的基石。安全員作為施工現場安全管理的直接責任人&#xff0c;其專業能力和資質認證顯得尤為重要。山東省安全員C證作為衡量安全員專業水平的重要標準&#xff0c;不僅關乎個人職業發展&#xff0c;更直…

Spring 中的 bean 生命周期

&#x1f331; 一、什么是 Bean 生命周期&#xff1f; 在 Spring 容器中&#xff0c;一個 Bean 從“創建 → 初始化 → 使用 → 銷毀”&#xff0c;經歷了完整的生命周期。 Spring 提供了 多個擴展點 讓你可以在這些階段做事情&#xff0c;比如注入資源、日志記錄、連接資源、清…

Media streaming mental map

Media streaming is a huge topic with a bunch of scattered technologies, protocols, and formats. You may feel like hearing fragments without seeing the big picture. Let’s build that mental map together — here’s a high-level overview that connects everyt…

AIDD-深度學習 MetDeeCINE 破譯代謝調控機制

深度學習 MetDeeCINE 破譯代謝調控機制 目錄 使用 FEP/REMD 和 DFT 方法準確預測藥物多靶點絕對結合自由能的新途徑。Scorpio 框架利用對比學習優化核苷酸序列表示&#xff0c;提升基因組分析效率&#xff0c;尤其在未知序列的分類和泛化能力上表現出色。LPM 模型整合多模態擾…

【2】搭建k8s集群系列(二進制)之安裝etcd數據庫集群

一、etcd服務架構 Etcd 是一個分布式鍵值存儲系統&#xff0c;Kubernetes 使用 Etcd 進行數據存儲&#xff0c;所以先 準備一個 Etcd 數據庫&#xff0c;為解決 Etcd 單點故障&#xff0c;應采用集群方式部署&#xff0c;這里使用 3 臺組建集群&#xff0c;可容忍 1 臺機器故障…

fastGPT—前端開發獲取api密鑰調用機器人對話接口(HTML實現)

官網文檔鏈接&#xff1a;OpenAPI 介紹 | FastGPT 首先按照文檔說明創建api密鑰 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-sca…

cpp自學 day19(多態)

一、基本概念 同一操作作用于不同的對象&#xff0c;產生不同的執行結果 &#x1f449; 就像「按F1鍵」&#xff1a;在Word彈出幫助文檔&#xff0c;在PS彈出畫筆設置&#xff0c;?同一個按鍵觸發不同功能 &#xff08;1&#xff09;多態類型 類型實現方式綁定時機?靜態多態…

Java 大視界 -- Java 大數據在航天遙測數據分析中的技術突破與應用(177)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…

人臉考勤管理一體化系統(人臉識別系統,簽到打卡)

人臉考勤管理一體化系統 項目介紹 本項目是基于Flask、SQLAlchemy、face_recognition庫的人臉考勤管理一體化系統。 系統通過人臉識別技術實現員工考勤打卡、人臉信息采集、人臉模型訓練等功能。 項目采用前后端分離的技術框架&#xff0c;基于Flask輕量級Web框架搭建后端服務…

單調棧學習C++

目錄 一&#xff0c;每日溫度 二&#xff0c;下一個更大的元素I 三&#xff0c;下一個更大的元素II 四&#xff0c;接雨水 小結&#xff1a; 單調棧是一種特殊的棧結構&#xff0c;里面的元素按照單調遞增或者遞減的順序排列。常用于解決元素左邊或者右邊比它大或者小的問…

網絡釣魚攻擊的威脅和執法部門的作用(第一部分)

在當今的數字世界中&#xff0c;網絡犯罪分子不斷開發新技術來利用個人、企業和政府機構。 最普遍和最具破壞性的網絡犯罪形式之一是網絡釣魚——一種社會工程手段&#xff0c;用于欺騙人們提供敏感信息&#xff0c;例如登錄憑據、財務數據和個人詳細信息。 隨著網絡釣魚攻擊…

左值與右值,空間與數據

左值是空間&#xff0c;右值是數據 編程總是對“數據”&#xff0c;對"存放數據的空間"操作 a返回一個當前的數據&#xff0c;存放到一個臨時空間中&#xff0c;自身的空間中的數據再進行運算 a直接對自身空間中的數據進行運算 其余知識&#xff1a; 1.變量名的意…

無人機飛行術語科普!

一、基礎操作類 1. 炸機 指無人機意外墜毀或嚴重損壞&#xff08;如撞樹、撞樓、失控摔機等&#xff09;。 例句&#xff1a;“今天風太大&#xff0c;差點炸機&#xff01;” 2. 一鍵放生 調侃某些情況下無人機失控飛丟&#xff0c;無法找回&#xff08;源自某些品牌…

模擬算法(一):一維數組模擬

目錄 模擬的概念 例1&#xff1a;開關燈 算法思路&#xff1a; 代碼如下&#xff1a; 輸入輸出&#xff1a; 例2&#xff1a;序列操作和查詢 算法思路&#xff1a; 代碼如下&#xff1a; 輸入輸出&#xff1a; 例3&#xff1a;數組折疊 算法思路&#xff1a; 代碼如…