Spring Boot 如何覆蓋自動配置
本文轉載自微信公眾號「七哥聊編程」,作者七哥。轉載本文請聯系七哥聊編程公眾號。
本文提供完整代碼示例,詳見 https://gitee.com/isevenluo/spring-road/ 的 spring-road03 目錄。
1. 緣起
眾所周知,Spring Boot 提供一個牛逼哄哄的特性,幫助我們少寫了很多模板化的配置代碼,這個特性就是:自動配置。
比如 Spring Data JPA,Spring Security 只要引入相關的依賴包,就會幫助我們自動配置好數據源 Bean 和 安全設置相關的 Bean,不用我們自己寫配置就實現了相應的功能。
但是具體應用中,我們的實際情況往往都是比較復雜的,僅僅通過 Spring 自動配置是不能滿足我們的需求的。就拿安全配置來說,在 Classpath 添加 Spring Security 后,默認為我們應用程序提供的安全設施是比較基礎且粗暴的。我們在 Web 中訪問應用程序,就會看到如下類似的身份驗證對話框:
此處的用戶名是 user,密碼就比較蛋疼了,在應用每次啟動時隨機生成寫入到日志里面了:
Spring Security 自動配置生成的密碼
雖然我們的應用已經算是一個安全的 Web 應用程序了,但是也有如下顯著缺點:
- 頁面太簡陋,這個對話框很不友好呀;
- 應用程序只有一個登錄用戶;
- 用戶密碼還要在應用啟動日志中查看(默認用戶名是user,密碼是在應用啟動時隨機生成寫入日志的);
那如何調整自動配置讓 Spring 按照我們的需要進行配置呢?別著急,我們接著往下看。
一般我們覆蓋 Spring Boot 默認自動配置的方式有兩種:自定義配置Bean、修改外置屬性進行配置。
2. 自定義配置Bean覆蓋自動配置
覆蓋自動配置的第一種方法呀,就是我們當自動配置不存在,自己手動配置對應的 Spring Bean。配置方式可以選擇 XML,也可以用 Java 形式的配置。
今天七哥就選擇目前更加流行的 Java 形式的配置,來演示一下如何覆蓋我們上面所描述的自動配置的 Spring Security。
我們所要做的很簡單,那就是寫一個擴展類。
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
- private Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
- @Resource
- private ViewerRepository viewerRepository;
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(username -> {
- viewerRepository.save(new Viewer(username, "{noop}123","ADMIN"));
- logger.info(viewerRepository.findViewerByUsername(username).toString());
- return viewerRepository.findViewerByUsername(username);
- });
- }
- }
上面的代碼中的 SecurityConfig 是個非常基礎的 Spring Security 配置,有了它我們的 Spring Boot 應用程序就跳過了安全自動配置,不再使用默認的用戶名和密碼,而是使用我們定義的用戶和密碼來鑒權。
當然上面的代碼展示的不全,但是剩下的類都不是重點,我還定義了一個 Viewer 類,實現了 UserDetails 類(Spring Security 中的用戶接口),還有一個 Spring Data JPA 倉庫接口,用來保存用戶信息。詳細的代碼,都已經上傳到開源倉庫了,有需要的小伙伴自取哦 ~
??? 這里我們再次強調一遍:想要覆蓋 Spring Boot 的自動配置,我們只需要寫一個顯式的配置,這樣 Spring Boot 就會發現我們的配置,然后就會降低自動配置的優先級,以我們自己寫的為準。
3. 通過配置屬性來調整自動配置
這種配置屬性文件的方式,相比于上面這種自定義 Bean 配置要簡單很多。比如假設我們僅僅要 調整一個數據庫的 URL、Spring Security 的默認用戶名、應用日志的級別,如果都像上面那樣覆蓋自動配置,自己完整的聲明一個 Bean,這就有點傻了!畢竟配置一個屬性要比完整寫一個 Bean 的配置要簡單的多。
那如何配置屬性呢?
Spring Boot 應用程序支持的配置源有很多,比如說環境變量、命令行參數,當然最常見的還是屬性文件里配置。
接下來給大家演示幾個很常見的例子。
- Spring Boot 應用程序啟動時,命令行會打印一個 Banner,如果你想禁用這個 Banner,可以將 spring.main.banner-mode 屬性設置為 off 即可;
添加屬性前:
在屬性文件 application.properties 中添加屬性 spring.main.banner-mode=off 后:
結果已經打開在控制臺了,啟動時的 Banner 已經不見了。
- 調整配置應用程序的日志;
Spring Boot 默認使用 Logback來記錄日志,并且用 INFO 級別輸出到控制臺。Logback一般情況下能很好的滿足我們的需要,但是這里為了演示,假如我們要使用 Log4j2 替換默認的 Logback實現,需要怎么配置呢?
以 Gradle 為例,我們只需要修改起步依賴,在構建說明文件中引入對應的日志實現然后排除掉 Logback。
- configurations {
- all*.exclude group:'org.springframework.boot' , module:'spring-boot-starter-logging'
- }
上面我們排除掉默認的日志依賴后,就可以引入我們需要的 Log4j2 日志依賴。
- dependencies {
- implementation 'org.springframework.boot:spring-boot-starter-log4j2'
- }
接下來如果還需要做一個常規的操作:修改日志級別和指定日志輸出的文件。
當然我們以往的做法就是自己添加一個 log4j2.xml 文件,用來配置日志相關的信息,雖然這樣可以讓我們完全掌握應用程序的日志配置,但是僅僅修改日志級別和日志輸出的文件則可以完全不用創建 log4j2.xml 文件,使用屬性配置就可以實現。
我們在 Spring Boot 應用程序的 application.properties 文件中加入 logging 開頭的屬性就可以滿足我們期望。
- // 指定日志級別為debug
- logging.level.root=debug
- // 指定日志文件路徑
- logging.file.name=/Users/sevenluo/IdeaProjects/spring-road/spring-labs/spring-road03/logs/spring-road03.log
至此,說明了通過上面的屬性配置也實現了我們對于 Spring Boot 應用程序自動配置的微調。
4. 總結
今天我們介紹了兩種覆蓋 Spring Boot 自動配置的方法。
第一種就是自定義配置 Bean,實現原理是通過 Spring 的條件化配置,這塊有一個非常重要的注解就是 @ConditionalOnMissingBean ,意思是如果我們的 classpath 中沒有發現相應類型的 Bean,Spring Boot 才會幫我們自動配置一個。Spring Boot 的設計是優先加載我們應用里面配置的類,然后在考慮自動配置類。
第二種就是通過配置屬性來調整 Spring Boot 自動配置,實現原理就是設定不同屬性源里配置的屬性優先級不同,而我們應用程序屬性文件中添加的屬性配置優先級高于默認屬性,所以實現了調整自動配置的目的。
今天就先聊到這里,更多關于 Spring 相關的內容,也在持續更新中,敬請關注!