在向應用程序加入Spring Boot時,有個名為spring-boot-autoconfigure的JAR文件,其中包含很多的配置類。所有的配置如此的與眾不同,原因是它們利用了Spring的條件化配置。條件化配置允許配置存在于應用程序中,但在滿足某些特定條件之前都忽略這個配置。
舉個例子:
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
...
}
以上是Spring Boot自動配置里的DataSourceAutoConfiguration的一個片段。DataSourceAutoConfiguration添加了@Configuration注解,表明這是一個配置類。最重要的是該類添加了@ConditionalOnClass注解,要求Classpath里必須要有DataSource和EmbeddedDatabaseType。如果他們不存在,條件就不成立,DataSourceAutoConfiguration提供的配置就會被忽略掉。
自動配置中使用的條件化注解:
- @ConditionalOnBean: 配置了某個特定Bean
- @ConditionalOnMissingBean: 沒有配置特定的Bean
- @ConditionalOnClass: Classpath里有指定的類
- @ConditionalOnMissingClass: Classpath里缺少指定的類
- @ConditionalOnExpression: 給定的SpEL表達式計算結果為true
- @ConditionalOnJava :Java的版本匹配特定值或者一個范圍值
- @ConditionalOnJndi: 參數(shù)中給定的JNDI位置必須存在一個,如果沒有參數(shù),則需要JNDI InitialContext
- @ConditionalOnProperty: 指定的配置屬性要有一個明確的值
- @ConditionalOnResource: Classpath里有指定的資源
- @ConditionalOnWebApplication: 這是一個Web應用程序
- @ConditionalOnNotWebApplication: 這不是一個Web應用程序
1.覆蓋Spring Boot 的自動配置
我們以Spring Security來舉例。想要使用Spring Security,首先在pom.xml添加如下依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
添加完依賴后,重構項目,此時該項目就可以運行。但訪問url地址時,它會提示你登錄。沒錯,此時只是用的Spring Security默認的配置,默認的配置可能滿足不了我們的需求,那么我們可以顯式地覆蓋Spring Security的自動配置。
默認的登錄頁面.png
覆蓋自動配置的顯示安全配置:
package com.jasonper.readinglist;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ReaderRepository readerRepository;
/**
* 此configure方法設置了一個自定義的userDetailsService,這個服務可以是任意實現(xiàn)了userDetailsService的類,用于查找指定用戶名的用戶。
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(username -> readerRepository.findOne(username));
}
/**
* 此configure方法指明:
* "/"的請求只有經過身份認證且具有READER角色的用戶采納訪問,其他的所有請求路徑向所有的用戶開放了訪問權限。
* 這里還指定了登錄頁面和登錄失敗頁面
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").access("hasRole('READER')")
.antMatchers("/**").permitAll()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true");
}
}
上面的例子足以演示如何覆蓋Spring Boot提供的安全自動配置。想要覆蓋Spring Boot的自動配置,我們所要做的僅僅是編寫一個顯示的java配置而已。
2.通過屬性文件外置配置
Spring Boot應用程序有多種設置途徑,包括如下幾處:
(1)命令行參數(shù)
(2)java:comp/env里的JNDI(Java Naming and Directory Interface)
(3)JVM系統(tǒng)屬性
(4)操作系統(tǒng)環(huán)境變量
(5)隨機生成的帶random.*前綴的屬性
(6)應用程序以外的application.properties或者application.yml文件
(7)打包在應用程序內的application.properties或者application.yml文件
(8)通過@PropertySource標注的屬性源
(9)默認屬性
Tips:上列表按照優(yōu)先級排序。
application.properties和application.yml文件能放在以下四個位置:
- 外置,在相對于應用程序運行目錄的/config子目錄里
- 外置,在應用程序運行的目錄里
- 內置,在config包內
- 內置,在Classpath根目錄
此外,如果你在同一優(yōu)先級位置同時有application.properties和application.yml,那么application.yml里的屬性會覆蓋application.properties里的屬性
- 自動配置微調
在Spring Boot里有300多個屬性可以用來微調應用程序里的Bean。
例如,通過application.yml來更改嵌入式服務器端口:
server:
port: 8000
- 應用程序的Bean的配置外置
假設我們在application.yml配置了如下信息:
person:
name: 張三
age: 20
想要在某個類上拿到剛剛所配置的信息,那很簡單,只需在類上配置如下注解,即可在該用的地方使用剛剛配置的屬性:
@ConfigurationProperties(prefix = "person")
當然,我們也可以在一個Bean里加載配置屬性:
package com.jasonper.readinglist;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("person") // 從application.yml里的person中獲得屬性
public class PersonProperties {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
- 使用Profile進行配置
當應用程序需要部署到不同的運行環(huán)境時,一些配置細節(jié)通常會有所不同。比如數(shù)據(jù)庫連接在開發(fā)環(huán)境和生產環(huán)境下就會不一樣,在測試環(huán)境下又不一樣。此時可以使用Profile。Profile是一種條件化配置,基于運行時激活的Profile,會使用或者忽略不同的Bean或配置類。
以Spring Security為例:
@Profile("production")
@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
...
}
這里的@Profile注解要求運行時激活production Profile,這樣才能應用該配置。激活配置的方式有很多種,這里使用application.yml文件來激活:
spring:
profiles:
active: production
- 使用特定于Profile的屬性文件
如果你正在使用application.properties,可以創(chuàng)建額外的屬性文件,遵循application-{profile}.properties這種命名格式,這樣就能提供特定于Profile的屬性了。
- 使用多Profile YAML文件進行配置
如果使用YAML來配置屬性,則可以遵循與配置文件相同的命名規(guī)范:application-{profile}.yml這樣的YAML文件,并將與Profile無關的屬性繼續(xù)配置在application.yml里。
同時,可以把所有的Profile的配置屬性都放在同一個application.yml文件里。使用日志配置來舉個例子:
logging:
level:
root: INFO
---
spring:
profiles: dev
logging:
level:
root: DEBUG
---
spring:
profiles: production
logging:
path: /tmp/
file: BookWorm.log
level:
root: WARN
這個application.yml分為三部分,使用一組三個連字符作為分隔符。第二段和第三段分別指定了不同的環(huán)境下的日志配置。第二段是開發(fā)環(huán)境下的,第三段是生產環(huán)境下的。
另一方面,第一段未指定spring.profile,因此這里的屬性對全部Profile都生效,或者對那些未設置該屬性的激活Profile生效。
3.定制應用程序的錯誤頁面
定制應用程序的錯誤頁面,只需在src/main/resources/templates中加入自己寫的錯誤頁面即可。Spring Boot會為錯誤視圖提供如下錯誤屬性:
- timestamp:錯誤發(fā)生的時間
- status:錯誤狀態(tài)碼
- error:錯誤的原因
- exception:異常的類型
- message:異常消息(如果這個錯誤是由異常引起的)
- errors:BindingResult異常里的各種錯誤(如果這個錯誤是由異常引起的)
- trace:異常跟蹤信息(如果這個錯誤是由異常引起的)
- path:錯誤發(fā)生時請求的url路徑