Spring Boot 3 的相關設定
設定類別
spring 3.0 以前,都是使用 XML 設定檔。3.0 以後,是透過 @Configuration
註解的類別做設定。
@SpringBootApplication
裡面包含了 @SpringBootConfiguration
,該設定可代替 @Configuration
example: @Bean
就類似以前 xml 的 <bean>
@SpringBootConfiguration
public class MainConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
SprintBoot 可使用一個 @SpringBootConfiguration
或是 @Configuration
類別做設定,但也可以區分不同功能的設定,然後再使用 @Import
彙整在一起。
@SpringBootConfiguration
@Import({Config1.class, Config2.class})
public class MainConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
但如果設定類別都在類別掃描路徑上,可以用 @ComponentScan
掃描所有 packages。因 @SpringBootConfiguration
包含了 @ComponentScan
,Application 以下的所有 packages。
也可以用 @ImportResource
匯入 XML 設定檔
設定檔
Spring Boot 的主要設定檔在 application.properties
如果有使用 Spring Cloud,有另一個 bootstrap.properties,該設定檔的優先權比 application.properties 高。
bootstrap 由 ApplicationContext 載入,優先權高
bootstrap 的設定不能被覆寫
設定檔可以用 .properties 或是 .yml (yaml 格式)
server.port = 8090
server.servlet.contextPath = /test
可使用 @PropertySource
匯入
.yml
server:
port: 8090
servlet:
contextPath: /test
可用 YamlPropertiesFactoryBean 轉換為 Properties,或是用 YamlMapFactoryBean 轉換為 Map。也可以用 YamlPropertySourceLoader 在入為 PropertySource
@ConfigurationProperties
支援兩種格式的設定檔
設定綁定
所有已載入 Spring 的設定,都可以用 Environment 取得
@Autowired
private Environment env;
//
String getProperties(String key);
使用 @Value
使用 @Value
${ property : default_value }
#{ obj.property ?: default_value }
example:
application.properties
jdbc.driverClass=com.mysql
在類別中,可用以下方式取得
@Value("${jdbc.driverClass}")
private String driver;
也可以用 @PropertySource
@Data
@Component
@PropertySource(value={"/config/db.properties"})
public class DBProperties {
@Value("${jdbc.driverClass}")
private String driverClass;
}
映射到類別、Constructor
可直接以 @ConfigurationProperties
將設定映射到一個類別
ex:
application.yaml
demo1:
name: test
users:
- user1
- user2
params:
place : userplace
demo2:
name: test2
age: 11
birthday: 2024/11/11 11:11:11
demo3:
name: test3
age: 22
Demo1.properties
package tw.com.test.demo1;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
import java.util.Map;
@Data
@ConfigurationProperties(prefix="demo1")
public class Demo1Properties {
private String name;
private List<String> users;
private Map<String, String> params;
}
Demo2Properties.java
package tw.com.test.demo1;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.ConstructorBinding;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@NoArgsConstructor
@ConfigurationProperties(prefix="demo2")
public class Demo2Properties {
private String name;
private int age;
private Date birthday;
@ConstructorBinding
public Demo2Properties(String name,
@DefaultValue("1") int age,
// @DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss") Date birthday,
@JsonFormat(pattern = "yyyy/MM/dd HH:mm:ss", timezone="GMT+8") Date birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
}
Demo1Application.java
package tw.com.test.demo1;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@RequiredArgsConstructor
@EnableConfigurationProperties(value = {Demo1Properties.class, Demo2Properties.class})
@Slf4j
public class Demo1Application {
private final Demo1Properties demo1Properties;
private final Demo2Properties demo2Properties;
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner() {
return (args) -> {
log.info("demo1 properties: {}", demo1Properties);
log.info("demo2 properties: {}", demo2Properties);
};
}
}
CommandLineRunner 的用途是,在 CLI 啟動 application 後,會被呼叫的 method
執行結果
demo1 properties: Demo1Properties(name=test, users=[user1, user2], params={place=userplace})
demo2 properties: Demo2Properties(name=test2, age=11, birthday=Mon Nov 11 11:11:11 CST 2024)
Bean 綁定
@ConfigurationProperties
除了用在 class,也可以用在 @Bean
MainConfig.java
@SpringBootConfiguration
public class MainConfig {
@Bean
@ConfigurationProperties(prefix="demo3")
public Demo3Properties demo3Properties() {
return new Demo3Properties();
}
}
Demo3Properties.java
package tw.com.test.demo1;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class Demo3Properties {
private String name;
private int age;
}
Demo1Application 不需要寫在 @EnableConfigurationProperties
@SpringBootApplication
@RequiredArgsConstructor
@EnableConfigurationProperties(value = {Demo1Properties.class, Demo2Properties.class})
@Slf4j
public class Demo1Application {
private final Demo1Properties demo1Properties;
private final Demo2Properties demo2Properties;
private final Demo3Properties demo3Properties;
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner() {
return (args) -> {
log.info("demo1 properties: {}", demo1Properties);
log.info("demo2 properties: {}", demo2Properties);
log.info("demo3 properties: {}", demo3Properties);
};
}
}
執行結果
demo3 properties: Demo3Properties(name=test3, age=22)
設定類別掃描
在 Application 加上 @ConfigurationPropertiesScan
可掃描該 package 以下的所有設定類別,如果要掃描特定package,就加上 basePackages
@SpringBootApplicaiton
@RequiredArgsConstructor
@ConfigurationPropertiesScan
public class Application {
}
設定驗證
使用 @ConfigurationProperties
可利用 JSR-303 javax.validation 驗證設定值
pom.xml 要加上套件
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
在設定欄位上加上 @NotNull
@Data
@Validated
@NoArgsConstructor
@ConfigurationProperties(prefix="demo2")
public class Demo2Properties {
@NotNull
private String name;
private int age;
private Date birthday;
外部設定
設定來源
properties
YAML
環境變數
CLI 參數
設定的優先等級
由低到高
預設參數 SpringApplication.setDefaultProperties
用
@PropertySource
綁定的設定套用 application 檔案的參數
設定了
random.*
的參數系統環境變數
Java System Properties
java:comp/env 的 JNDI 參數
ServletContext 初始化參數
ServletConfig 初始化參數
來自 SPRING_APPLICATION_JSON 的參數
CLI 參數
單元測試裡面的參數
使用
@TestPropertySource
綁定的設定Devtools 全域設定參數,來自
$HOME/.config/spring-boot
多個設定檔案的優先等級,低到高
jar 裡面的設定檔
指定了 profile 的設定檔,ex: application-dev.properties (jar 裡面)
application 設定檔 (jar 外面)
指定了 profile 的設定檔,ex: application-dev.properties (jar 外面)
CLI 參數,是啟動時,用 --
開頭的參數
ex:
java -jar application.jar --server.port=8090
用 maven 啟動
mvn spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver.port=8090'
如果不希望把CLI 參數增加到 spring 環境中,可以禁用
public static void main(String[] args) {
SpringApplication.setAddCommandLineProperties(false);
SpringApplication.run(Application.class, args);
}
匯入設定檔
可利用 spring.config.import 指定匯入設定檔的路徑
spring:
config:
import:
- optional:classpath:/config/app.yml
random
RandomVlauePropertySource 可用來注入亂數值,可以產生 int, long, uuid, string
ex:
demo1:
age: ${random.int[10,100]}
security:
security-key: ${random.value}
security-code: ${random.uuid}
多個設定環境
有可能遇到 正式、測試、開發環境不同的設定
yaml 用 --- 區隔
ex:
spring:
application:
name: "Demo1"
---
spring:
application:
name: "Production1"
config:
activate:
on-cloud-platform: "kubernates"
properties
spring.application.name=Demo1
#---
spring.application.name=Production1
config.activate.on-cloud-platform=kubernates
以下設定檔,可限制只在 dev 或是 test 環境使用
spring.config.activate.on-profile
spring.config.activate.on-cloud-platform
spring:
profiles:
active: dev
---
spring:
config:
activate:
on-profile: "dev|test"
Profile
profile可以分離設定
如果不指定時,就會使用預設的 Profiledefault,可以修改預設 profile
spring:
profiles:
default: dev
啟用 profile
啟用 dev, test
spring:
profiles:
active: dev, test
也可以在程式裡面啟用
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(Demo1Application.class);
springApplication.setAdditionalProfiles("dev", "test");
springApplication.run(args);
}
profile 可啟用設定跟 bean,可用在 @Component
, @Configuration
, @ConfigurationProperties
ex: 以下設定只有在啟用 "main" 時,才會載入這個設定
@Profile("main")
@SpringBootConfiguration
public class MainConfig {
}
yaml
spring:
profiles:
default: dev
active: dev, main
把 @Profile
用在 @ConfigurationProperties
時要注意
如果是用掃描的註冊方式,
@Profile
可直接用在@ConfigurationProperties
如果是用
@EditConfigurationProperties
,則要把@Profile
用在@EditConfigurationProperties
設定類別上
切換 Profile
spring.profiles.active
設定啟用的 profile
但還是能在 CLI 參數調整,因為 CLI 參數優先權比設定檔高
java -jar application.jar --spring.profiles.active=test
mvn spring-boot:run -Dapp.profiles=test
如果不希望被替代,可改用 include
spring:
profiles:
default: dev
active: dev, main
# include 的 profile 不會被覆蓋
include:
- dev
- main
profile 分組
在 yaml 將 profile 分組,當 main 啟用時, main1, main2 也會被啟用
spring:
profiles:
default: dev
active: dev, main
# include 的 profile 不會被覆蓋
include:
- dev
- main
group:
main:
- main1
- main2
spring:
config:
active:
on-profile: main1
---
spring:
config:
active:
on-profile: main2
指定 profile 的設定檔
application-${profile}
ex:
application.yml
application-dev.yml
application-test.yml
application-main.yml
active: dev, main
優先順序 default > dev > main
使用限制
application.profiles.default
不指定 profile 的預設 profile
application.profiles.active
啟用的 profiles
application.profiles.include
要包含的 profiles,不會被 CLI 參數覆蓋
application.profiles.group
分組
這些參數不能用在多個設定環境跟指定 profile 設定檔
ex: 以下是錯誤用法
在 application.yml
---
---
spring:
config:
activate:
on-profile: main2
profiles:
active: main
在 application-dev.yml 使用 application.profiles.active
spring:
profiles:
active: main
設定加密
Spring Boot 沒有提供標準加解密的方法,但有提供 EnvironmentPostProcessor interface,可在 application 啟動前控制 spring 環境
spring 加解密的方法:
使用第三方設定中心,支援自動解密~~~~
使用自訂加解密
使用 Jasypt Spring Boot
第三方設定
Spring Cloud Config
需要加解密的內容,前面會加上 {cipher}
自訂加解密
模仿 spring cloud 的方法
spring:
datasource:
username: '{cipher}XXXXXXXX'
用以下方式解密
@Bean
public DataSource dataSource() {
DataSource dataSource = new DruidDataSource();
String username = this.getUsername();
if( username.startWith("{cipher}") ) {
username = Encrypt.decrypt( username, this.getKey() );
}
dataSource.setUsername(username);
//...
return dataSource;
}
使用 Jasypt Spring Boot
ref: GitHub - ulisesbocchio/jasypt-spring-boot: Jasypt integration for Spring boot
ref: Spring boot 開發 - 使用 Jasypt 進行加密 | roi's blog
可針對 Spring Boot 專案中的屬性,提供加解密的方案
設定升級
當 Spring Boot 更新版本後,某些設定參數可能已經被修改或刪除,在 pom 加上這個 library 就能自動分析。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
</dependency>
在啟動後,就可在 console 看到需要調整的參數
在程式啟動後,再加入 Spring 的設定參數,這個自動分析不支援。ex:
@PropertySource
載入的設定設定修改完成後,要將此 library 移除
沒有留言:
張貼留言