【Spring Boot】第6課-在 application.properties 配置檔提供設定值(以 Java Mail 為例)
本文最後更新於:2025-03-22
在串接資料庫或其他第三方服務時,通常都需要那些平台的伺服器位址、帳密,或其他設定值,我們合稱為「連線字串」(connection string)。
若我們希望在不同環境啟動程式時(如測試環境、正式環境),能彈性切換不同的連線字串,那麼可以將這些值寫在一個叫「配置檔」的地方,再由程式去取用。
本文會以使用透過 Java Mail 寄送郵件為例,示範如何配置設定值,並利用 Profile 的功能來切換。最後講解在啟動 JAR 檔時,如何引入配置檔。
本文的練習用專案,請點我。
一、準備程式專案
(一)範例專案介紹
在本文的練習用專案,能找到筆者事先準備好的範例程式。
第一個是「MailController」,裡面有一支 RESTful API。我們預期它會接收 request,並執行一段寄送 email 的程式。
@RestController
public class MailController {
@PostMapping("/mail")
public ResponseEntity<Void> sendPlainText(@RequestBody SendMailRequest request) {
// TODO
return ResponseEntity.noContent().build();
}
}
第二個是「SendMailRequest」,它會在 controller 接收 request body,包含 email 的收件人、主旨與內容。
public class SendMailRequest {
private String[] receivers = new String[0];
private String subject = "";
private String content = "";
// getter, setter ...
}
(二)匯入函式庫
請在 pom.xml 檔案添加「Spring Mail」的依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
這項依賴其實是 Spring Boot 對 Java Mail 進行封裝後的產物,讓我們容易使用。
二、取得 Google 應用程式密碼
由於本文會搭配郵件服務來做示範,因此需要做一些事前準備。畢竟在程式中串接外部服務時,通常都需要那個平台的帳密。
筆者會使用 Gmail 來寄信,然而 Google 為了安全性考量,不允許我們直接在程式中使用 Google 密碼。因此接下來請依照步驟,產生「應用程式密碼」。
在 Google 帳戶的首頁,前往「安全性」的頁籤,找到「登入 Google 的方式」,進入「兩步驟驗證」畫面。
從畫面最下方,進入「應用程式密碼」畫面。
在畫面中為這次要建立的密碼取個名字。
按下「建立」後,即可得到 16 個字的應用程式密碼。
請讀者將這組密碼記在別的地方,因為離開此畫面後,便無法再看到了。
三、認識 Spring Mail 的用法
本節會展示如何使用 Spring Mail 發送純文字郵件,藉此讓讀者知道使用 Gmail 寄信時,需要哪些設定值。到了第四節,我們再調整成使用「配置檔」的做法。
@RestController
public class MailController {
@PostMapping("/mail")
public ResponseEntity<Void> sendPlainText(@RequestBody SendMailRequest request) {
JavaMailSenderImpl sender = createMailSender();
SimpleMailMessage msg = new SimpleMailMessage();
msg.setFrom(String.format("%s<%s>", "Spring Mail", sender.getUsername()));
msg.setTo(request.getReceivers());
msg.setSubject(request.getSubject());
msg.setText(request.getContent());
sender.send(msg);
return ResponseEntity.noContent().build();
}
private JavaMailSenderImpl createMailSender() {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
// 郵件服務主機
sender.setHost("smtp.gmail.com");
sender.setPort(587);
// 郵件服務帳密
sender.setUsername("your_gmail");
sender.setPassword("your_application_password");
Properties props = sender.getJavaMailProperties();
props.put("mail.smtp.auth", true); // 是否向郵件服務驗證身份
props.put("mail.smtp.starttls.enable", true); // 是否啟用 TLS(傳輸層安全),對通訊加密
props.put("mail.transport.protocol", "smtp"); // 傳輸協定
return sender;
}
}
上面的範例程式中,在「createMailSender」方法建立了 JavaMailSenderImpl
物件。它需要一些有關郵件服務的參數。例如主機、port 號、帳密等。
而 API 的處理方法「sendPlainText」,則建立了 SimpleMailMessage
物件,並填寫郵件的寄件人、收件人、主旨與內容,最後寄出。
此處呼叫 setFrom
方法,在寄件人給予 名稱<信箱>
格式的字串。這是為了讓收件人能看到我們自定義的寄件人名稱,而不是一個 email 地址。
完成後,讀者可用 Postman 之類的工具呼叫該 API。
POST http://localhost:8080/mail
{
"receivers": ["foo@gmail.com", "bar@gmail.com"],
"subject": "Hello",
"content": "World"
}
確認一下目前的程式能夠寄信到指定的 email 地址。
四、開始使用配置檔
(一)撰寫參數
在第三節,我們把寄信的相關設定值寫死在程式碼中(hard code),這樣的做法可能有一些缺點:
- 當各種服務的連線字串,都散落在專案的不同地方,以後若想查看或修改,就不容易找到當初是寫在哪裡。
- 承上,即使將設定值集中寫在一個 Java 類別作為常數,但每次修改後,都得重新打包出 JAR 檔,才能交付。
- 為了便於管理,我們可以將設定值集中寫在一個叫做「application.properties」的配置檔中。該檔案預設是位於「src\main\resources」資料夾下。若讀者沒有該檔案,請自行建立一個。
接著將寄信時所用到的設定值,以 key=value
的格式寫在裡面,以下是一個範例:
mail.host=smtp.gmail.com
mail.port=587
mail.username=your_gmail
mail.password=your_application_password
mail.transport-protocol=smtp
mail.smtp.auth=true
mail.starttls.enable=true
mail.display-name=Spring Mail
此處的 key 都是筆者自己取名,且單字可用「.」符號來表示階層。
(二)讀取配置檔參數
將設定值都寫到 application.properties 配置檔後,我們就能在元件中透過 @Value
注解,來讀取它們。
@RestController
public class MailController {
@Value("${mail.host}")
private String host;
@Value("${mail.port}")
private int port;
@Value("${mail.username}")
private String username;
@Value("${mail.password}")
private String password;
@Value("${mail.transport-protocol}")
private String protocol;
@Value("${mail.smtp.auth}")
private boolean smtpAuth;
@Value("${mail.starttls.enable}")
private boolean enableStartTls;
@Value("${mail.display-name:Test Mail}")
private String displayName;
@PostMapping("/mail")
public ResponseEntity<Void> sendPlainText(@RequestBody SendMailRequest request) {
// ...
msg.setFrom(String.format("%s<%s>", displayName, sender.getUsername()));
// ...
}
private JavaMailSenderImpl createMailSender() {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost(host);
sender.setPort(port);
sender.setUsername(username);
sender.setPassword(password);
Properties props = sender.getJavaMailProperties();
props.put("mail.smtp.auth", smtpAuth);
props.put("mail.smtp.starttls.enable", enableStartTls);
props.put("mail.transport.protocol", protocol);
return sender;
}
}
使用 @Value
注解時,需傳入配置檔的 key 名稱。若有需要,讀者也可提供預設值。格式為 ${key 名稱:預設值}
。
(三)封裝到配置類別
也許讀者已經感受到了,當類別中存在太多 @Value
注解,整個類別就會很冗長。
因此,我們也可以考慮將讀取設定值的部份,轉移到另一個元件類別。
@Configuration
public class MailConfig {
@Value("${mail.host}")
private String host;
@Value("${mail.port}")
private int port;
@Value("${mail.username}")
private String username;
@Value("${mail.password}")
private String password;
@Value("${mail.transport-protocol}")
private String protocol;
@Value("${mail.smtp.auth}")
private boolean smtpAuth;
@Value("${mail.starttls.enable}")
private boolean enableStartTls;
@Value("${mail.display-name:Test Mail}")
private String displayName;
// getter ...
}
以上建立一個叫做「MailConfig」的類別,並冠上 @Configuration
注解,代表這是與配置有關的元件類別。另外也提供 getter 方法供外部呼叫。
之後便可將這個元件注入到需要的地方。
@RestController
public class MailController {
@Autowired
private MailConfig mailConfig;
@PostMapping("/mail")
public ResponseEntity<Void> sendPlainText(@RequestBody SendMailRequest request) {
// ...
msg.setFrom(String.format("%s<%s>", mailConfig.getDisplayName(), sender.getUsername()));
// ...
}
private JavaMailSenderImpl createMailSender() {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost(mailConfig.getHost());
sender.setPort(mailConfig.getPort());
sender.setUsername(mailConfig.getUsername());
sender.setPassword(mailConfig.getPassword());
Properties props = sender.getJavaMailProperties();
props.put("mail.smtp.auth", mailConfig.isSmtpAuth());
props.put("mail.smtp.starttls.enable", mailConfig.isEnableStartTls());
props.put("mail.transport.protocol", mailConfig.getProtocol());
return sender;
}
}
五、使用函式庫所指定的設定值
Spring Mail 函式庫本身有提供「自動配置」的功能。也就是說,Spring Mail 自己也有一個類似第四節的 MailConfig 配置類別。它會讀取配置檔中指定 key 名稱的設定值,私下建立出 JavaMailSenderImpl
元件。
這麼一來,我們其實直接取用它所建立出來的元件即可,完全不需要自己寫程式來建立。
本節讓我們稍作調整,將配置檔的設定值,改為 Spring Mail 指定的 key 名稱。
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=your_gmail
spring.mail.password=your_application_password
spring.mail.properties.mail.transport.protocol=smtp
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
mail.display-name=Spring Mail
到這邊為止,除了「mail.display-name」的 key 名稱是筆者自己取名,其餘都是 Spring Mail 特別指定的。
接下來,請在程式中放心地注入 JavaMailSenderImpl
元件。
@RestController
public class MailController {
@Autowired
private JavaMailSenderImpl sender;
@Autowired
private MailConfig mailConfig;
@PostMapping("/mail")
public ResponseEntity<Void> sendPlainText(@RequestBody SendMailRequest request) {
// ...
msg.setFrom(String.format("%s<%s>", mailConfig.getDisplayName(), sender.getUsername()));
// ...
sender.send(msg);
return ResponseEntity.noContent().build();
}
}
而先前用來手動建立 JavaMailSenderImpl
物件的 createMailSender 方法,則可以移除了。
六、使用 Profile 切換配置參數
(一)部署的環境
根據筆者的工作經驗,寫好的程式至少會在以下環境運行。
- 開發環境:在開發期間,讓工程師確認實作出的功能是否如預期。
- 測試環境:讓測試人員驗證功能是否正常。
- 正式環境:提供服務給真實使用者。
每個環境所需要的設定值可能不同。最典型的例子就是不同環境所使用的資料庫位址和帳密,都不一樣。
針對這個問題,我們可以準備多個配置檔,將不同環境的設定值寫在裡面。並搭配 Spring Boot 提供的「Profile」功能,切換使用不同配置檔。
(二)切換配置
以下準備了 2 份配置檔,分別用於開發和測試環境。檔名需遵循 application-環境名稱.properties 的格式。
「application-dev.properties」檔案:
mail.display-name=Spring Mail (dev)
「application-test.properties」檔案:
mail.display-name=Spring Mail (test)
它們都只有一個叫做「mail.display-name」的設定值,會做為寄件者的名稱。
接著在原本的 application.properties 配置檔中,添加 spring.profiles.active
的設定值,就能指定啟動程式時要用哪一份配置檔了。
spring.profiles.active=dev
# 其他參數 ...
此處的設定值為「dev」,因此 Spring Boot 便會去讀取「application-dev.properties」檔案。至於其他未寫在該配置檔的設定值,則會回頭採用 application.properties。
七、啟動 JAR 檔時指定配置檔
第六節的做法適合在開發工具中,切換不同的配置。而本節將說明如何在啟動 JAR 檔時,指定配置檔。
(一)指定 Profile
前面所建立的那些配置檔,會連同程式專案一起被打包進 JAR 檔。因此在 command line 執行 JAR 檔時,可提供 -Dspring.profiles.active
這項參數,指定要採用的配置檔。
java -Dspring.profiles.active=test -jar demo.jar
此處指定「test」這個 profile,代表要採用「application-test.properties」配置檔。
(二)自行提供配置檔
若讀者自己有準備一份配置檔,也可於啟動 JAR 檔時進行指定。
請在 JAR 檔的所在位置,建立一個叫做「config」的資料夾,並將配置檔放到裡面。相對位置如下:
|_ demo.jar
|_ config
|_ application.properties
啟動時,只要簡單地執行以下指令即可,Spring Boot 將會優先讀取 config 資料夾的配置檔。
java -jar demo.jar
八、YAML 檔
YAML 檔是一種常被用來配置設定值的檔案格式,副檔名為「yml」。我們可將 application.properties 檔案,改寫為「application.yml」檔案。Spring Boot 支援以 YAML 檔做為設定值的來源。
server:
port: 8080
spring:
profiles:
active: dev
mail:
host: smtp.gmail.com
port: 587
username: your_gmail
password: your_application_password
properties:
mail:
transport:
protocol: smtp
smtp:
auth: true
starttls:
enable: true
mail:
display-name: Spring Mail
從排版格式便可清楚地看出階層之分。YAML 檔以換行加兩個半形空格縮排為一個階層,相當於 properties 檔的「.」。YAML 格式有時具有更好的可讀性。
本文的完成後專案,請點我。