SpringBoot基礎
什麼是 Spring Boot?
SpringBoot是一個簡化 Spring 應用程序開發的框架,它的主要目標是減少 Spring 應用程序的配置和開發複雜性,使我們能夠更快地構建、測試和部署 Spring 應用。簡單來説,它通過提供默認配置、自動化配置和嵌入式服務器等功能,簡化了傳統Spring 應用的繁瑣配置過程。有人將一些依賴關係、默認配置都梳理好了,我們直接一個引用就搞定了,這就是它的本質。
Springboot的優點
- 內置Web容器:內置servlet容器,不需要在服務器部署 tomcat。只需要將項目打成 jar 包,使用 java -jar xxx.jar一鍵式啓動項目
- 內置Starter和自動配置:SpringBoot提供了starter,把常用庫聚合在一起,簡化複雜的環境配置,快速搭建spring應用環境
- 零XML配置: Spring Boot採用JavaConfig的方式進行開發,不需要編寫大量的XML配置文件。這種零XML的開發方式讓開發更加簡潔和可讀,同時提高了可維護性。
- 微服務支持: Spring Boot與Spring Cloud結合使用,可以輕鬆快速構建和部署微服務架構。
- 依賴版本管理: Spring Boot幫助開發人員管理了常用第三方依賴的版本,防止出現版本衝突問題。這樣,您可以更專注於業務邏輯,而不用擔心依賴的版本兼容性。
- 監控和管理: Spring Boot自帶了監控功能,包括應用程序運行狀況監控、內存使用情況、線程池狀態、HTTP請求統計等。此外,Spring Boot還提供了優雅關閉應用程序的方式,使得應用程序的管理更加便捷。
- SpringBoot總結就是使編碼變簡單、配置變簡單、部署變簡單、監控變簡單等等
Spring Boot 需要獨立的容器運行嗎?
可以不需要,內置了 Tomcat/ Jetty 等容器。
Javaweb、spring、springmvc和springboot有什麼區別,都是做什麼用的?
JavaWeb是 Java 語言的 Web 開發技術,主要用於開發 Web 應用程序,包括基於瀏覽器的客户端和基於服務器的 Web 服務器。
Spring是一個輕量級的開源開發框架,主要用於管理 Java 應用程序中的組件和對象,並提供各種服務,如事務管理、安全控制、面向切面編程和遠程訪問等。它是一個綜合性框架,可應用於所有類型的 Java 應用程序。
SpringMVC是 Spring 框架中的一個模塊,用於開發 Web 應用程序並實現 MVC(模型-視圖-控制器)設計模式,它將請求和響應分離,從而使得應用程序更加模塊化、可擴展和易於維護。
Spring Boot是基於 Spring 框架開發的用於開發 Web 應用程序的框架,它幫助開發人員快速搭建和配置一個獨立的、可執行的、基於 Spring 的應用程序,從而減少了繁瑣和重複的配置工作。
綜上所述,JavaWeb是基於 Java 語言的 Web 開發技術,而 Spring 是一個綜合性的開發框架,SpringMVC用於開發 Web 應用程序實現 MVC 設計模式,而 Spring Boot 是基於 Spring 的 Web 應用程序開發框架。
運行 SpringBoot 有哪幾種方式?
相關原理可以查看:兩種方式啓動原理
- IDEA中main函數啓動
- 用 Maven/Gradle 插件運行
- java -jar XXX.jar
Spring Boot 打成的 jar 和普通的 jar 有什麼區別 ?
- Spring Boot 項目最終打包成的 jar 是可執行 jar ,這種 jar 可以直接通過
java -jar xxx.jar命令來運行,這種 jar 不可以作為普通的 jar 被其他項目依賴,即使依賴了也無法使用其中的類。 - Spring Boot 的 jar 無法被其他項目依賴,主要還是他和普通 jar 的結構不同。普通的 jar 包,解壓後直接就是包名,包裏就是我們的代碼,而 Spring Boot 打包成的可執行 jar 解壓後,在
\BOOT-INF\classes目錄下才是我們的代碼,因此無法被直接引用。如果非要引用,可以在 pom.xml 文件中增加配置,將 Spring Boot 項目打包成兩個 jar ,一個可執行,一個可引用。
為什麼SpringBoot的jar可以直接運行?
Spring Boot的可執行JAR文件之所以可以直接運行,原因如下:
- Spring Boot提供了一個Maven插件(spring-boot-maven-plugin),用於將應用程序打包成可執行的JAR文件。通過執行mvn clean package等命令,可以輕鬆生成可執行JAR。
- 打包生成的JAR文件通常是"Fat JAR"或"Uber JAR",這意味着它包含了應用程序的所有依賴項,包括第三方庫和Spring Boot框架本身。這樣,JAR文件就成了一個自包含的單一文件。
- JAR文件包含一個名為MANIFEST.MF的清單文件,其中包含了關於JAR文件的元數據信息。其中,主要的信息是Main-Class,它指定了啓動應用程序的主類。
- Spring Boot的可執行JAR文件通常由JarLauncher類啓動。JarLauncher負責創建一個類加載器(LaunchedURLClassLoader),加載boot-lib目錄下的JAR文件,包括Spring Boot loader相關的類。然後,它在一個新線程中啓動應用程序的Main方法,實現應用程序的啓動。、
- 當執行Main方法最終會加載Spring容器、進而創建內嵌Tomcat進行阻塞線程使我們jar包完成web應用的啓動
SpringBoot 常用的 Starter 有哪些?
- spring-boot-starter-web :提供 Spring MVC + 內嵌的 Tomcat 。
- spring-boot-starter-data-jpa :提供 Spring JPA + Hibernate 。
- spring-boot-starter-data-Redis :提供 Redis 。
- mybatis-spring-boot-starter :提供 MyBatis 。
SpringBoot 中的 starter 到底是什麼 ?
- 個人理解SpringBoot就是由各種Starter組合起來的,我們自己也可以開發Starter
- 在sprinBoot啓動時由@SpringBootApplication註解會自動去maven中讀取每個starter中的spring.factories文件,該文件裏配置了所有需要被創建spring容器中的bean,並且進行自動配置把bean注入SpringContext中 //(SpringContext是Spring的配置文件)
- 首先它提供了一個自動化配置類,一般命名為 XXXAutoConfiguration ,在這個配置類中通過條件註解來決定一個配置是否生效(條件註解就是 Spring 中原本就有的),然後它還會提供一系列的默認配置,也允許開發者根據實際情況自定義相關配置,然後通過類型安全的屬性(spring.factories)注入將這些配置屬性注入進來,新注入的屬性會代替掉默認屬性。正因為如此,很多第三方框架,我們只需要引入依賴就可以直接使用了。當然,開發者也可以自定義 Starter
SpringBoot如何自定義Starter
在開發分佈式Springboot項目時, 自定義Starter是一定會用到的。以下是創建自定義Spring Boot Starter的基本步驟:
- 創建項目結構: 創建一個Maven或Gradle項目,確保項目結構符合標準的約定。通常,項目結構包括src/main/java用於存放Java代碼和src/main/resources用於存放資源文件。
- 編寫自動配置類: 創建一個自動配置類,該類負責配置自定義Starter的功能。在自動配置類上使用@Configuration註解,並通過其他註解如@ConditionalOnClass、@ConditionalOnProperty等來定義條件,以確保只有在滿足特定條件時才會應用配置。
- 提供屬性配置: 如果您的Starter需要配置屬性,可以在src/main/resources/application.properties或src/main/resources/application.yml中定義屬性。這些屬性可以在自動配置類中使用@Value註解注入。
- 創建META-INF/spring.factories文件: 在項目的資源目錄中創建META-INF/spring.factories文件。在這個文件中,註冊您的自動配置類,以便Spring Boot能夠自動識別和加載它。
- 定義Starter依賴: 在自定義Starter的pom.xml文件中,定義Spring Boot的核心依賴以及您的Starter所依賴的其他庫。
- 測試和文檔: 編寫單元測試和集成測試,以確保自定義Starter的功能和配置正確。同時,提供詳細的文檔和示例,以便用户能夠正確配置和使用您的Starter。
- 發佈到倉庫: 將自定義Starter打包,併發布到Maven中央倉庫或私有倉庫,以便其他項目可以引入和使用。
自定義一個Spring Boot Starter需要遵循上述步驟,其中創建META-INF/spring.factories文件是關鍵,因為它告訴Spring Boot如何自動裝配您的功能。這樣,其他項目可以方便地引入您的Starter,實現功能的快速集成。
SpringBoot與SpringCloud 區別
SpringBoot是快速開發的Spring框架,SpringCloud是完整的微服務框架,SpringCloud依賴於SpringBoot。
SpringBoot註解
Spring Boot 的核心註解是哪個?
啓動類上面的註解是@SpringBootApplication,它也是 Spring Boot 的核心註解,主要組合包含了以下 3 個註解:
- @SpringBootConfiguration:組合了 @Configuration 註解,實現配置文件的功能。
- @EnableAutoConfiguration:打開自動配置的功能,也可以關閉某個自動配置的選項,如關閉數據源自動配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
- @ComponentScan:Spring組件掃描。
有哪些常用的SpringBoot註解?
- @SpringBootApplication:這個註解是Spring Boot最核心的註解,用在 Spring Boot的主類上,標識這是一個 Spring Boot 應用,用來開啓 Spring Boot 的各項能力
- @SpringBootConfiguration:組合了 @Configuration 註解,實現配置文件的功能。
- @EnableAutoConfiguration:打開自動配置的功能,也可以關閉某個自動配置的選項,如關閉數據源自動配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。
- @ComponentScan:Spring組件掃描。
- @Repository:用於標註數據訪問組件,即DAO組件。
- @Service:一般用於修飾service層的組件
- @RestController:用於標註控制層組件(如struts中的action),表示這是個控制器bean,並且是將函數的返回值直 接填入HTTP響應體中,是REST風格的控制器;它是@Controller和@ResponseBody的合集。
- @ResponseBody:表示該方法的返回結果直接寫入HTTP response body中
- @Component:泛指組件,當組件不好歸類的時候,我們可以使用這個註解進行標註。
- @Bean:相當於XML中的
<bean></bean>,放在方法的上面,而不是類,意思是產生一個bean,並交給spring管理。 - @AutoWired:byType方式。把配置好的Bean拿來用,完成屬性、方法的組裝,它可以對類成員變量、方法及構造函數進行標註,完成自動裝配的工作。
- @Qualifier:當有多個同一類型的Bean時,可以用@Qualifier("name")來指定。與@Autowired配合使用
- @Resource(name="name",type="type"):沒有括號內內容的話,默認byName。與@Autowired幹類似的事。
- @RequestMapping:RequestMapping是一個用來處理請求地址映射的註解;提供路由信息,負責URL到Controller中的具體函數的映射,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。
- @RequestParam:用在方法的參數前面。
- @RequestBody:將 HTTP 請求體中的數據綁定到方法參數上。Spring 會將JSON、XML或其他格式的請求體轉換為Java 對象,並將其傳遞給控制器方法的參數。
- @PathVariable:Spring MVC 中用於從URI 模板中提取變量值的註解。它的主要作用是在處理 HTTP請求時,從請求的URL 路徑中捕獲變量,並將其綁定到控制器方法的參數上
- @Scope:用於聲明一個Spring
Bean實例的作用域 - @Primary:當同一個對象有多個實例時,優先選擇該實例。
- @PostConstruct: 用於修飾方法,當對象實例被創建並且依賴注入完成後執行,可用於對象實例的初始化操作。
- @PreDestroy:用於修飾方法,當對象實例將被Spring容器移除時執行,可用於對象實例持有資源的釋放。
- @EnableTransactionManagement:啓用Spring基於註解的事務管理功能,需要和
@Configuration註解一起使用。 - @Transactional:表示方法和類需要開啓事務,當作用與類上時,類中所有方法均會開啓事務,當作用於方法上時,方法開啓事務,方法上的註解無法被子類所繼承。
- @ControllerAdvice:常與
@ExceptionHandler註解一起使用,用於捕獲全局異常,能作用於所有controller中。 - @ExceptionHandler:修飾方法時,表示該方法為處理全局異常的方法。
- @Profile:用於定義一組 Bean 的配置文件所屬的環境,比如 dev 通常表示開發環境,prod 表示生產環境
@Validated 和 @Valid 註解有什麼區別?
@Validated 和 @Valid 都是用於在 Spring 中執行對象驗證的註解,但它們的使用場景和特性有一些區別:
- @Validated:這是標準的 Java Bean Vlidation註解,來自javax.vlidation註解。它通常用於方法參數或類的字段上,以觸發基於註解的驗證規則,如(@NotNull、@Size等);在Sping中,它可以用於驗證單個對象或嵌套對象。
- @Valid:這是 Sping 特有的註解,來自org.springframework.vlidation.annotation.Vlidated包,它的主要作用是支持分組驗證(Group Vlidation),允許開發者根據不同的場景定義不同的驗證邏輯。它也可以用在類級別、方法參數上,觸發不同驗證組的規則。
@Value註解的原理
在 Spring 框架中,@Vàlue 註解用於注入外部化的配置值到 Spring 管理的 Bean 中。通過 @Value 註解,可以將屬性文件、環境變量、系統屬性等外部資源中的值注入到 Spring Bean 的字段、方法參數或構造函數參數中。
@Value的解析就是在bean初始化階段。BeanPostProcessor定義了bean初始化前後用户可以對bean進行操作的接口方法,它的一個重要實現類AutowiredAnnotationBeanPostProcessor為bean中的@Autowired和@Value註解的注入功能提供支持。
@PropertySource 註解的作用是什麼?
是 Spring 中用於加載外部屬性文件(如.properties 文件)的註解。
它的主要作用是讓 Spring 應用程序可以從外部的屬性文件中讀取配置,並將這些屬性注入到 Spring 的 Environment 中,從而實現應用的外部化配置,使得應用程序在不同環境下更容易管理和維護。方便通過@Value 或 Environment 對象獲取屬性值
@Scheduled 註解的作用是什麼?
這個註解用於在 Spring 應用中定時執行方法。它可以將某個方法標記為一個定時任務,並根據設定的時間間隔、固定速率、或 Cron 表達式來定時觸發該方法的執行。
主要作用:
- 定時任務執行: @Scheduled 註解允許開發者定義一個方法,該方法會按照指定的時間規則定期執行。
- 支持多種時間配置:支持固定延遲、固定速率以及基於Cron 表達式的任務調度。
@Cacheable 和 @CacheEvict 註解的作用是什麼?
@Cacheable 和 @CacheEvict 是Spring中用於緩存操作的兩個重要註解,主要用於提高系統性能,通過減少對數據庫等外部資源的頻繁訪問。
- @Cacheable:用於將方法的返回結果緩存起來。下次再調用相同參數的方法時,直接從緩存中獲取結果,而不是重新執行該方法。
-
@CacheEvict:用於從緩存中移除一項或多項數據,通常在更新或刪除操作時使用,確保緩存中的數據保持一致性。
@Conditional 註解的作用是什麼?
作用於有條件地裝配 Bean。
可以根據特定的條件來決定某個 Bean 是否應該被加載到 Spring 容器中。例如可以根據環境(如開發、測試、生產)或特定上下文條件動態裝配 Bean,實現動態加載
主要作用:
- 有條件地加載 Bean:@Conditional 根據某個條件來決定某個 Bean 是否需要注入到 Spring 容器中。條件可以是操作系統類型、類路徑是否存在某個類、某個屬性的值等。
- 實現動態配置:可以根據環境(如開發、測試、生產)或特定上下文條件動態裝配 Bean,避免不必要的 Bean 被加載。
@Lazy 註解的作用是什麼?
@Lazy 的兩種用法:
- 和註解 @Component 或 @Bean 一起使用,可以延遲 Bean 的創建時機,當用到這個 Bean 的時候(依賴注入、從 Beanfactory直接獲取),才進行創建
- 和註解 @Autowire 一起使用,Spring 將注入一個代理類
@EventListener 註解的作用是什麼?
通過標註在方法上, @EventListener 可以使方法自動監聽特定類型的事件,它用於監聽和處理事件。並在事件發佈時觸發執行,它提供了一種鬆耦合的方式來處理應用中的事件,避免事件發佈者和監聽者之間的直接依賴關係,
使用場景:
- 當系統中某個狀態變化時觸發特定操作。
- 日誌記錄、監控系統、通知系統等需要響應事件的模塊。
@Async 註解的原理是什麼?
@Async 註解的核心原理是基於Spring AOP的動態代理機制,結合線程池實現異步任務調度,通過合理的線程池配置和異常處理,可以高效地實現異步操作。
@Async 什麼時候會失效?
@Async失效的主要原因包括內部調用繞過代理、方法非 public、未啓用異步支持、返回值類型不匹配、異常處理不當、線程池配置錯誤以及對象未被 Spring 管埋等
-
內部調用:@Async 依賴於 Spring 的動態代理機制(AOP),而內部調用會繞過代理對象,直接調用原始方法。解決如下:
- 將異步方法提取到獨立的Bean中。
- 使用 Aopcontext.currentProxy()獲取代理對象調用。
- 注入自身Bean並通過其調用異步方法
- 非公共方法:Spring AOP 默認只對 public方法生效,非 public 方法不會被代理解決。需要確保被@Async標註的方法是public.
- 沒有啓用異步支持:@Async 需要顯式啓用異步功能,否則 Spring 不會為其生成代理解決。在 Spring Boot 應用的主類或配置類上添加 @EnableAsync 註解。
- 方法返回值不匹配:@Async 對返回值類型有一定要求,某些情況下可能導致行為異常。需要確保返回值類型符合 @Async 的要求。(返回值為 void、CompletableFuture等)Future
- 異常處理不當:異步方法中的異常不會直接傳播到調用方,可能導致問題被忽略。需要使用 try-catch 捕獲異常或者配置全局異常處理器
- 自定義線程池配置錯誤:如果自定義線程池配置不當,可能導致異步任務無法正常執行。比如沒有正確註冊或配置線程池。正確配置並註冊 TaskExecutor 就可以了。
- Spring上下文未加載:@Async 依賴 Spring 容器管理的 Bean,如果對象沒有被 Spring 管理,那麼代理機制就會失效。確保 Bean 由 Spring 容器管理,不要手動去 new。
什麼是 JavaConfig?
Spring JavaConfig 是 Spring 社區的產品,Spring 3.0引入了他,它提供了配置 Spring IOC 容器的純Java 方法。因此它有助於避免使用 XML 配置。
使用 JavaConfig 的優點在於:
- 面向對象的配置。由於配置被定義為 JavaConfig 中的類,因此用户可以充分利用 Java 中的面向對象功能。一個配置類可以繼承另一個,重寫它的@Bean 方法等。
- 減少或消除 XML 配置。基於依賴注入原則的外化配置的好處已被證明。但是,許多開發人員不希望在 XML 和 Java 之間來回切換。JavaConfig 為開發人員提供了一種純 Java 方法來配置與 XML 配置概念相似的 Spring 容器。從技術角度來講,只使用 JavaConfig 配置類來配置容器是可行的,但實際上很多人認為將JavaConfig 與 XML 混合匹配是理想的。
- 類型安全和重構友好。JavaConfig 提供了一種類型安全的方法來配置 Spring容器。由於 Java5.0 對泛型的支持,現在可以按類型而不是按名稱檢索 bean,不需要任何強制轉換或基於字符串的查找。
常用的Java config:
- @Configuration:在類上打上寫下此註解,表示這個類是配置類
- @ComponentScan:在配置類上添加 @ComponentScan 註解。該註解默認會掃描該類所在的包下所有的配置類,相當於之前的
<context:component-scan >。 - @Bean:bean的注入:相當於以前的
< bean id="objectMapper"class="org.codehaus.jackson.map.ObjectMapper" /> - @EnableWebMvc:相當於xml的`<mvc:annotation-driven >
-
@ImportResource: 相當於xml的
< import resource="applicationContextcache.xml">實現原理
SpringBoot為什麼默認使用CGLIB
SpringBoot默認使用CGLIB 原因如下:
- 無需接口: CGLIB能夠代理那些沒有實現接口的類,而JDK動態代理只能代理實現了接口的類。這使得Spring Boot可以更靈活地使用代理,而無需依賴於接口。
- AOP支持: Spring Boot廣泛使用AOP(面向切面編程)來處理日誌、事務、安全性等橫切關注點。CGLIB更適合創建AOP代理,因為它可以代理普通的類而不僅僅是接口,在開發中如果通過反射獲得代理目標方法的註解,如果用JDK動態代理將導致無法獲取。
- 可以代理本類方法:這意味着即使在同一個類中調用了另一個方法,仍然可以觸發代理的行為。這對於某些特定的AOP需求非常有用,因為它允許您在同一類中的方法之間應用切面。這種能力被稱為"自我調用"或"內部調用"的代理。
- 方法調用性能: 一旦代理對象創建完成,實際的方法調用性能可能會因代理方式而異。在JDK 1.8之後,JDK動態代理的方法調用性能相對較好,但CGLIB仍然可能更快,因為CGlib是直接調用父類方法即目標方法,無需像JDK代理還要通過反射進行內部方法棧調用才能到目標方法。
SpringBoot的啓動原理?
- 啓動 main()方法:應用從 main()方法啓動,並通過SpringApplication.run()引|導應用啓動。
- 創建SpringApplication:應用會創建 springApplication 對象,推斷應用類型、設置初始化器、設置啓動監聽器、、確定主應用類。
- 準備環境(ConfigurableEnvironment):Spring Boot 在啓動過程中準備應用環境,加載配置文件、系統環境變量以及命令行參數。
- 創建井刷新 ApplicationContext:創建應用上下文,加載配置類和自動配置類,註冊 Bean 並執行依賴注入等初始化操作。
- 在刷新上下文時啓動嵌入式 Web 服務器“對於 Web 應用,Spring Boot 會自動啓動嵌入式 Web 容器(如 Tomcat),並註冊相關的 Servlet 和 filter。
- 發佈應用己啓動事件:對應監聽 stated 事件邏輯會被觸發。
- 執行CommandLineRunner和ApplicationRunner:在應用啓動完成後,執行實現了commandLineRunner和ApplicationRunner接口的初始化邏輯。
- 發佈 ready 事件、應用啓動完成:觸發 ApplicationReadyEvent ,應用進入運行狀態,處理業務請求或任務
SpringBoot 是如何通過 main 方法啓動 web 項目的?
SpringBoot 應用的啓動流程都封裝在 SpringApplication.run方法中,它的大部分邏輯都是複用 Spring 啓動的流程,只不過在它的基礎上做了大量的擴展在啓動的過程中有一個刷新上下文的動作,這個方法內會觸發 webserver 的創建,此時就會創建並啓動內嵌的 web服務,默認的 web 服務就是 tomcat
Spring Boot 的啓動過程幾個核心步驟:
- SpringApplication.run():這是啓動的入口,它會創建 Spring 應用上下文,並執行自動配置。
- 創建應用上下文:為 Web 應用創建 AnnotationConfigServletWebServerApplicationContext 上下文
- 啓動內嵌 Web 服務器:在 refreshContext0) 階段啓動內嵌的 Web 服務器(如Tomcat)。
- 自動配置:通過 @EnableAutoConfiquration 自動配置各種組件,如 DispatcherServlet
- 請求處理:內嵌的 DispatcherServet 負責處理 HTTP 請求。
自動配置原理
在 application.properties 中設置屬性 debug=true,可以在控制枱查看已啓用和未啓用的自動配置。
@SpringBootApplication是@Configuration、@EnableAutoConfiguration和@ComponentScan的組合。
@Configuration表示該類是Java配置類。
@ComponentScan開啓自動掃描符合條件的bean(添加了@Controller、@Service等註解)。
@EnableAutoConfiguration會根據類路徑中的jar依賴為項目進行自動配置,比如添加了spring-boot-starter-web依賴,會自動添加Tomcat和Spring MVC的依賴,然後Spring Boot會對Tomcat和Spring MVC進行自動配置(spring.factories EnableAutoConfiguration配置了WebMvcAutoConfiguration)。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
EnableAutoConfiguration主要由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)這兩個註解組成的。
@AutoConfigurationPackage用於將啓動類所在的包裏面的所有組件註冊到spring容器。
@Import 將EnableAutoConfigurationImportSelector注入到spring容器中,EnableAutoConfigurationImportSelector通過SpringFactoriesLoader從類路徑下去讀取META-INF/spring.factories文件信息,此文件中有一個key為org.springframework.boot.autoconfigure.EnableAutoConfiguration,定義了一組需要自動配置的bean。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
這些配置類不是都會被加載,會根據xxxAutoConfiguration上的@ConditionalOnClass等條件判斷是否加載,符合條件才會將相應的組件被加載到spring容器。(比如mybatis-spring-boot-starter,會自動配置sqlSessionFactory、sqlSessionTemplate、dataSource等mybatis所需的組件)
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
AnnotatedElement.class }) //類路徑存在EnableAspectJAutoProxy等類文件,才會加載此配置類
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
全局配置文件中的屬性如何生效,比如:server.port=8081,是如何生效的?
@ConfigurationProperties的作用就是將配置文件的屬性綁定到對應的bean上。全局配置的屬性如:server.port等,通過@ConfigurationProperties註解,綁定到對應的XxxxProperties bean,通過這個 bean 獲取相應的屬性(serverProperties.getPort())。
//server.port = 8080
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
private Integer port;
private InetAddress address;
@NestedConfigurationProperty
private final ErrorProperties error = new ErrorProperties();
private Boolean useForwardHeaders;
private String serverHeader;
//...
}
簡單總結如下:
- 主要是Spring Boot的啓動類上的核心註解SpringBootApplication註解主配置類,有了這個主配置類啓動時就會為SpringBoot開啓一個@EnableAutoConfiguration註解自動配置功能。
- @EnableAutoConfiguration引入了@Import<這意味着它會導入其他配置類,這些配置類包含了Spring Boot自動配置的邏輯。
- 當Spring容器啓動時,會解析@Import註解,並加載相應的配置。
-
接着就會:
- 讀取META-INF/spring.factories文件:從配置文件META_INF/Spring.factories加載可能用到的自動配置類
- 過濾出AutoConfigurationClass:去重,並將exclude和excludeName屬性攜帶的類排除
- 條件化加載:過濾,將滿足條件(@Conditional)的自動配置類返回
實現自動配置
實現當某個類存在時,自動配置這個類的bean,並且可以在application.properties中配置bean的屬性。
(1)新建Maven項目spring-boot-starter-hello,修改pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tyson</groupId>
<artifactId>spring-boot-starter-hello</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>1.3.0.M1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
</project>
(2)屬性配置
public class HelloService {
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String sayHello() {
return "hello" + msg;
}
}
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix="hello")
public class HelloServiceProperties {
private static final String MSG = "world";
private String msg = MSG;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
(3)自動配置類
import com.tyson.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(HelloServiceProperties.class) //1
@ConditionalOnClass(HelloService.class) //2
@ConditionalOnProperty(prefix="hello", value = "enabled", matchIfMissing = true) //3
public class HelloServiceAutoConfiguration {
@Autowired
private HelloServiceProperties helloServiceProperties;
@Bean
@ConditionalOnMissingBean(HelloService.class) //4
public HelloService helloService() {
HelloService helloService = new HelloService();
helloService.setMsg(helloServiceProperties.getMsg());
return helloService;
}
}
- @EnableConfigurationProperties 註解開啓屬性注入,將帶有@ConfigurationProperties 註解的類注入為Spring 容器的 Bean。
- 當 HelloService 在類路徑的條件下。
- 當設置 hello=enabled 的情況下,如果沒有設置則默認為 true,即條件符合。
- 當容器沒有這個 Bean 的時候。
(4)註冊配置
想要自動配置生效,需要註冊自動配置類。在 src/main/resources 下新建 META-INF/spring.factories。添加以下內容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.tyson.config.HelloServiceAutoConfiguration
"\"是為了換行後仍然能讀到屬性。若有多個自動配置,則用逗號隔開。
(5)使用starter
在 Spring Boot 項目的 pom.xml 中添加:
<dependency>
<groupId>com.tyson</groupId>
<artifactId>spring-boot-starter-hello</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
運行類如下:
import com.tyson.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class SpringbootDemoApplication {
@Autowired
public HelloService helloService;
@RequestMapping("/")
public String index() {
return helloService.getMsg();
}
public static void main(String[] args) {
SpringApplication.run(SpringbootDemoApplication.class, args);
}
}
在項目中沒有配置 HelloService bean,但是我們可以注入這個bean,這是通過自動配置實現的。
在 application.properties 中添加 debug 屬性,運行配置類,在控制枱可以看到:
HelloServiceAutoConfiguration matched:
- @ConditionalOnClass found required class 'com.tyson.service.HelloService' (OnClassCondition)
- @ConditionalOnProperty (hello.enabled) matched (OnPropertyCondition)
HelloServiceAutoConfiguration#helloService matched:
- @ConditionalOnMissingBean (types: com.tyson.service.HelloService; SearchStrategy: all) did not find any beans (OnBeanCondition)
可以在 application.properties 中配置 msg 的內容:
hello.msg=Seven
SpringBoot的使用
Spring Boot 中application.properties 和 application.yml 的區別是什麼?
它們兩者的區別就在於書寫格式,對配置而言效果是一樣的,就是個人偏好問題。
- application.properties 使用鍵值對配置,鍵和值之間用等號或冒號分隔
- application.yml 使用 YAML (YAML Aint Markup Language)格式,具有層級結構,使用縮進表示嵌套關係。適合複雜配置,閲讀性更佳。
SpringBoot為什麼要禁止循環依賴
循環依賴大家都知道,也被折磨過,在 SpringBoot2.6.0的版本默認禁止了循環依賴,如果程序中出現循環依賴就會報錯。
當然並沒有一錘子打死,也提供了開啓允許循環依賴的配置,只需要在配置文件中開啓即可:
spring:
main:
allow-circular-references: true
那SpringBoot為什麼要要禁止呢?我們都知道Spring解決循環依賴的方式是通過三級緩存,光學這個三級緩存我們就煞費苦心,其實説白了他是一種給程序員擦屁股的行為.
其實對象之間的關係如果是互相依賴是一種不合理的設計,避免你做出這種不合理的依賴,SpringBoot進而禁止循環依賴。
SpringBoot可以同時處理多少請求?
詳情請看Tomcat線程池詳解
SpringBoot默認的內嵌容器是Tomcat,也就是我們的程序實際上是運行在Tomcat裏的。所以與其説SpringBoot可以處理多少請求,到不如説Tomcat可以處理多少請求。
在SpringBoot中處理請求數量相關的參數有四個:
- server.tomcat.threads.min-spare:最少的工作線程數,默認大小是10。該參數相當於長期工,如果併發請求的數量達不到10,就會依次使用這幾個線程去處理請求。
- server.tomcat.threads.max:最多的工作線程數,默認大小是200。該參數相當於臨時工,如果併發請求的數量在10到200之間,就會使用這些臨時工線程進行處理。
- server.tomcat.max-connections:最大連接數,默認大小是8192。表示Tomcat可以處理的最大請求數量,超過8192的請求就會被放入到等待隊列。
- server.tomcat.accept-count:等待隊列的長度,默認大小是100。
Spring Boot 支持哪些日誌框架?
Spring Boot 支持 Java Util Logging, Log4j2, Lockback 作為日誌框架,如果你使用 Starters 啓動器,Spring Boot 將使用 Logback 作為默認日誌框架,但是不管是那種日誌框架他都支持將配置文件輸出到控制枱或者文件中。
SpringBoot事務的使用
SpringBoot的事物很簡單,首先使用註解EnableTransactionManagement開啓事物之後,然後在Service方法上添加註解Transactional便可。
Async異步調用方法
在SpringBoot中使用異步調用是很簡單的,只需要在方法上使用@Async註解即可實現方法的異步調用。 注意:需要在啓動類加入@EnableAsync使異步調用@Async註解生效。
為啥不建議用BeanUtils.copyProperties拷貝數據?
-
屬性類型不一致導致拷貝失敗
- 同一屬性的類型不同:例如ID,可能在Source類中定義的類型為Long,在Target類中定義的類型為String,此時如果使用BeanUtils.copyProperties進行拷貝,就會出現拷貝失敗的現象,導致對應的字段為null
- 同一字段分別使用包裝類型和基本類型:例如Source類中定義為Long,在Target類中定義為long,在沒有傳遞實際值的時候,會出現異常
- null值覆蓋導致數據異常:Source數據裏面如果某些字段有null值存在,但是對應的需要被拷貝過去的數據的相同字段的值並不為null,如果直接使用 BeanUtils.copyProperties 進行數據拷貝,就會出現Source數據的null值覆蓋Target的字段,導致原有的數據失效。
- 內部類數據無法成功拷貝:內部類數據無法正常拷貝,即使類型和字段名均相同也無法拷貝成功
- BeanUtils.copyProperties是淺拷貝:一旦在拷貝後修改了原始對象的引用類型的數據,就會導致拷貝數據的值發生異常,這種問題排查起來也比較困難。
- 底層實現為反射拷貝效率低
那應該如何解決?
- 手動複製:手動編寫
setter方法進行屬性複製。這種方式雖然代碼量較大,但能提供最好的性能和靈活性 - 構造函數或靜態工廠方法:通過構造函數或靜態工廠方法來初始化對象的屬性,確保對象在創建時就處於合法狀態。
- 使用Lombok的@Builder(推薦):可以利用
@Builder註解提供的Builder模式來簡化對象創建和屬性賦值。 -
ModelMapper或MapStruct:
- ModelMapper:一個靈活的對象映射庫,適合需要處理複雜映射關係的場景。
- MapStruct:一個代碼生成庫,在編譯時生成類型安全且高性能的映射代碼,適合需要高性能的場景。
如何在 Spring Boot 啓動的時候運行一些特定的代碼?
類似的問題:希望將數據庫中已有的固定內容,打入到 Redis 緩存中,請問如何處理?
- 實現接口 ApplicationRunner 或者 CommandLineRunner 的run方法,這兩個接口實現方式一樣,它們都只提供了一個 run 方法
- 使用 @PostConstruct 註解:在服務類中通過@PostConstruct註解標記初始化方法,在 Bean 創建後立即執行數據加載。
- 使用 InitializingBean 接口:InitializingBean 接口提供了 afterPropertiesSet 方法,用於在 Spring 容器初始化 bean 的屬性後,執行特定的初始化邏輯
- 使用 Spring 事件監聽器:可以通過監聽 Spring 的 ContextRefreshedEvent 期事件,在應用啓動時執行特定代碼。
- 自定義BeanFactoryPostProcessor和BeanPostProcessor:它們都是 Spring 容器啓動給的擴展點,可以在 Spring 容器初始化 bean 之前或之後執行特定邏輯。
- 使用 @Cacheable 註解:通過 @cacheable在首次調用方法時觸發緩存寫入,但需手動觸發首次調用才能完成預加載,
Spring Boot 有哪幾種讀取配置的方式?
簡單來看可以有三種:
-
使用 @Value 註解:
@Value("${my.custom.property}") private String myProperty; -
使用 @ConfigurationProperties 註解:
@Component @ConfigurationProperties(prefix = "my.custom") public class MyCustomProperties { private String property; } -
使用 Environment 接口:
@Autowired private Environment env; public void someMethod() { String value = env.getProperty("my.custom.property"); }
YAML 配置的優勢在哪裏 ?
YAML 配置和傳統的 properties 配置相比之下,有這些優勢:
- 配置有序
- 簡潔明瞭,支持數組,數組中的元素可以是基本數據類型也可以是對象
缺點就是不支持 @PropertySource 註解導入自定義的 YAML 配置。
Spring Boot 是否可以使用 XML 配置 ?
Spring Boot 推薦使用 Java 配置而非 XML 配置,但是 Spring Boot 中也可以使用 XML 配置,通過 @ImportResource 註解可以引入一個 XML 配置。
spring boot 核心配置文件是什麼?bootstrap.properties 和application.properties 有何區別 ?
單純做 Spring Boot 開發,可能不太容易遇到 bootstrap.properties 配置文件,但是在結合Spring Cloud 時,這個配置就會經常遇到了,特別是在需要加載一些遠程配置文件的時侯。
spring boot 核心的兩個配置文件:
- bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加載的,比applicaton 優先加載,配置在應用程序上下文的引導階段生效。一般來説我們在 SpringCloud 配置就會使用這個文件。且 boostrap 裏面的屬性不能被覆蓋;
- application (. yml 或者 . properties): 由ApplicatonContext 加載,用於 spring boot 項目的自動化配置。
Spring Boot 配置文件加載優先級你知道嗎?
簡單優先級:命令行參數 > JAR包外的 application-{profile}.properties > JAR包外的 aplitcation.properties > JAR包內的 aplication-{profile}.properties > JAR包內的aplication.properties
注意:當 application.properties和 application.yml 同時存在,同樣的參數,最終生效的是 application.properties 中的配置
Spring Boot 在啓動時加載配置屬性的完整優先級順序可參考如下的官方文檔:從上到下,優先級逐漸降低,即下面的配置,同樣的參數會被上面的配置所覆蓋
什麼是 Spring Profiles?
在項目的開發中,有些配置文件在開發、測試或者生產等不同環境中可能是不同的,例如數據庫連接、redis的配置等等。那我們如何在不同環境中自動實現配置的切換呢?Spring給我們提供了profiles機制給我們提供的就是來回切換配置文件的功能
Spring Profiles 允許用户根據配置文件(dev,test,prod 等)來註冊 bean。因此,當應用程序在開發中運行時,只有某些 bean 可以加載,而在 PRODUCTION中,某些其他 bean 可以加載。假設我們的要求是 Swagger 文檔僅適用於 QA 環境,並且禁用所有其他文檔。這可以使用配置文件來完成。Spring Boot 使得使用配置文件非常簡單。
在 Spring Boot 中如何實現多數據源配置?
- 在配置文件中定義多數據源
- 為每個數據源配置 DataSource、SqlSessionFactory和TransactionManager
- 為每個數據源配置獨立的 Mapper 掃描路徑
- 使用 @Transactional 指定事務管理器
SpringBoot多數據源拆分的思路
先在properties配置文件中配置兩個數據源,創建分包mapper,使用@ConfigurationProperties讀取properties中的配置,使用@MapperScan註冊到對應的mapper包中 。
SpringBoot多數據源事務如何管理
第一種方式是在service層的@TransactionManager中使用transactionManager指定DataSourceConfig中配置的事務。
第二種是使用jta-atomikos實現分佈式事務管理。
spring-boot-starter-parent 有什麼用 ?
新創建一個 Spring Boot 項目,默認都是有 parent 的,這個 parent 就是 spring-boot-starter-parent ,spring-boot-starter-parent 主要有如下作用:
- 定義了 Java 編譯版本。
- 使用 UTF-8 格式編碼。
- 執行打包操作的配置。
- 自動化的資源過濾。
- 自動化的插件配置。
- 針對 application.properties 和 application.yml 的資源過濾,包括通過 profile 定義的不同環境的配置文件,例如 application-dev.properties 和 application-dev.yml。
總結就是打包用的
Spring Boot 中如何解決跨域問題 ?
跨域可以在前端通過 JSONP 來解決,但是 JSONP 只可以發送 GET 請求,無法發送其他類型的請求,在 RESTful 風格的應用中,就顯得非常雞肋,因此我們推薦在後端通過 (CORS,Crossorigin resource sharing) 來解決跨域問題。這種解決方案並非 Spring Boot 特有的,在傳統的SSM 框架中,就可以通過 CORS 來解決跨域問題
- 局部配置 CORS:在 Controller 上使用 @CrossOrigin 註解。這種方式簡單直接,適合對類進行跨域設置。
- 全局配置 CORS:通過實現WebMvcConfigurer接口然後重寫addCorsMappings方法解決跨域問題。可以對整個應用程序進行統一的跨域配置
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.maxAge(3600);
}
}
Spring Boot 中的監視器是什麼?
Spring boot actuator 是 spring 啓動框架中的重要功能之一。Spring boot 監視器可幫助您訪問生產環境中正在運行的應用程序的當前狀態。有幾個指標必須在生產環境中進行檢查和監控。即使一些外部應用程序可能正在使用這些服務來向相關人員觸發警報消息。監視器模塊公開了一組可直接作為 HTTP URL 訪問的REST 端點來檢查狀態。
在 Spring Boot 中你是怎麼使用攔截器的?
在項目中我通常是使用攔截器來進行權限校驗、日誌記錄、處理異常等問題。
具體實現如下:
- 實現 Handlerinterceptor 接口,並實現接口中的方法,方法裏面包括了幾個請求時間點:請求前、請求後、整個請求結束後(用於資源清理等操作)。
- 通過實現 WebMvcConfigurer 的 addInterceptors 方法來添加自定義的攔截器。
如何使用 Spring Boot 實現全局異常處理?
Spring 提供了一種使用 ControllerAdvice 處理異常的非常有用的方法。 我們通過實現一個ControlerAdvice 類,來處理控制器類拋出的所有異常。
Spring Boot 中如何實現定時任務 ?
在 Spring Boot 中使用定時任務主要有兩種不同的方式,一個就是使用 Spring 中的 @Scheduled註解,另一個則是使用第三方框架 Quartz。使用 Spring 中的 @Scheduled 的方式主要通過 @Scheduled 註解來實現。
説説你對 Spring Boot 事件機制的瞭解?
Spring Boot 的事件機制,實際上是基於 Spring 的事件機制實現的,通過發佈-訂閲模式,主要用於應用程序中各個組件之間進行消息傳遞和解耦,通過事件發佈和監聽機制,實現了不同組件之間的鬆耦合,簡化模塊化開發和維護,例如我們可以通過監聽 Spring 應用上下文的啓動和關閉事件,進行相應的初始化和清理操作,而不需要修改 sping 源碼
作用總結:
- 解耦:通過事件機制,可以在不同組件之間傳遞消息,而不需要它們之間有直接的依賴關係,從而提高了代碼的可維護性和擴展性。
- 異步處理:某些事件可以異步處理,從而提高應用程序的響應速度和性能。
- 狀態通知:通過事件機制,可以通知應用程序的不同部分發生了某些特定的狀態變化,比如啓動完成、環境準備就緒等。