在現代應用程式開發中,我們需要管理大量的設定資訊,例如資料庫連線帳密、第三方服務的 API 金鑰 (API Key),或是在不同環境(如開發、測試、生產環境)中需要切換的開關。如果將這些資訊「寫死 (Hard-code)」在程式碼裡,會帶來一場災難。
想像一下,您將資料庫密碼直接寫在程式碼中:
為了解決這些問題,Spring 框架 (Spring Framework) 提供了強大而優雅的解決方案:@Value
註解 (Annotation)。它能幫助我們將設定值從程式碼中分離,注入到我們的 Spring Bean 元件 (Spring Bean Component) 中,實現所謂的「外部化設定 (Externalized Configuration)」。
@Value
最核心的用途,就是讀取專案中 src/main/resources/
目錄下的 application.properties
或 application.yml
檔案中的設定值。
application.properties
中的設定值這是 @Value
最常見、最直接的用法。我們使用 ${...}
的語法來指定要讀取的屬性鍵名 (Property Key)。
$
符號代表:我要從「設定檔」中拿一個值。
假設我們的 application.properties
檔案內容如下:
# application.properties
app.name=我的第一個 Spring Boot 應用程式
app.version=1.0.0
app.port=8080
app.enabled=true
現在,我們可以在一個 Spring 元件中,輕鬆地將這些值注入進來:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class AppInfoService {
// 將 app.name 的值注入到 appName 變數中
@Value("${app.name}")
private String appName;
@Value("${app.version}")
private String appVersion;
// Spring 會自動將 "8080" 這個字串轉換為 int 型別
@Value("${app.port}")
private int appPort;
// Spring 會自動將 "true" 這個字串轉換為 boolean 型別
@Value("${app.enabled}")
private boolean isEnabled;
public void printAppInfo() {
System.out.println("應用程式名稱: " + appName);
System.out.println("應用程式版本: " + appVersion);
System.out.println("通訊埠: " + appPort);
System.out.println("是否啟用: " + isEnabled);
}
}
當 Spring 容器 (Spring Container) 初始化 AppInfoService
這個 Bean 時,@Value
就會像一個魔法師,自動從設定檔中找到對應的鍵,並將其值賦予給標記的屬性。Spring Boot 的自動型別轉換功能非常強大,能處理大部分的基本資料型態。
如果在設定檔中找不到對應的屬性,又沒有提供預設值,那麼應用程式在啟動時會拋出 IllegalArgumentException
錯誤而失敗。為了增加程式的健壯性(Robustness),我們可以提供一個預設值。
語法非常簡單,在鍵名後面加上冒號 :
即可。 ${app.key:defaultValue}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class AnotherService {
// 如果在設定檔中找不到 'app.author',authorName 的值將會是 "預設作者"
@Value("${app.author:預設作者}")
private String authorName;
// 預設值也適用於數字等其他型別
@Value("${app.timeout:5000}")
private int timeout;
public void printDetails() {
System.out.println("作者: " + authorName);
System.out.println("超時設定 (ms): " + timeout);
}
}
這樣一來,即使 application.properties
中沒有 app.author
和 app.timeout
的設定,應用程式也能順利啟動並使用我們提供的預設值。
如果設定檔中的值是以逗號 ,
分隔的列表,@Value
也可以輕鬆地將它注入為陣列或 List
集合。
application.properties
:
app.servers=192.168.1.1,192.168.1.2,10.0.0.1
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Arrays;
@Component
public class ServerConfig {
// 直接注入為 List<String>
@Value("${app.servers}")
private List<String> serverList;
// 也可以注入為 String[] 陣列
@Value("${app.servers}")
private String[] serverArray;
public void printServers() {
System.out.println("伺服器列表 (List): " + serverList);
System.out.println("伺服器陣列 (Array): " + Arrays.toString(serverArray));
}
}
@Value
的另一面:SpEL (Spring Expression Language)@Value
的能力不僅限於讀取設定檔。它還能結合 Spring 表達式語言 (Spring Expression Language, SpEL),讓您執行更複雜的動態操作。
對於初學者來說,最重要的是要區分 ${}
和 #{}
的不同:
重點區分
$ { ... }
:用於讀取外部設定檔 (Properties) 的值。這是最常用、最基礎的用法。# { ... }
:用於執行 Spring 表達式語言 (SpEL),功能更為強大與動態,例如引用其他 Bean 的屬性或方法。
#
符號代表:我要執行一個「表達式 (Expression)」。
範例 1:引用系統屬性
您可以直接讀取 Java 虛擬機 (Java Virtual Machine, JVM) 的系統屬性。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class SystemInfoService {
// 使用 SpEL 讀取 Java Home 路徑 (這是一個 JVM 系統屬性)
@Value("#{systemProperties['java.home']}")
private String javaHome;
public void printSystemInfo() {
System.out.println("Java Home Path: " + javaHome);
}
}
範例 2:引用其他 Bean 的屬性
假設您有另一個 Bean,@Value
也可以透過 SpEL 直接引用它的屬性或調用它的方法。
首先,我們定義一個被引用的 Bean:
import org.springframework.stereotype.Component;
@Component("someBean") // 給這個 Bean 一個明確的名字 "someBean"
public class SomeBean {
public String getSomeValue() {
return "這是一個來自 SomeBean 的值";
}
}
然後,在另一個 Bean 中使用 SpEL 來引用它:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class SpELService {
// 引用名為 'someBean' 的 Bean,並調用它的 getSomeValue() 方法
// SpEL 會自動對應到 getSomeValue() 這個 getter
@Value("#{someBean.someValue}")
private String valueFromOtherBean;
public void printValue() {
System.out.println("從其他 Bean 獲取的值: " + valueFromOtherBean);
}
}
SpEL 的功能遠不止於此,但對於初學者而言,理解它可以動態執行程式碼片段,並與 ${}
做出區分,是邁向進階的關鍵第一步。
這份講義涵蓋了 @Value
從基礎到進階的核心用法。掌握它,您就能夠撰寫出更乾淨、更安全、也更具彈性的 Spring Boot 應用程式。
用途 | 語法 | 範例 | 說明 |
---|---|---|---|
基本屬性注入 | ${key} |
@Value("${app.name}") |
從設定檔讀取鍵 (Key) 為 app.name 的值。 |
提供預設值 | ${key:defaultValue} |
@Value("${app.author:Guest}") |
如果 app.author 不存在,則使用 Guest 作為預設值。 |
SpEL 表達式 | #{expression} |
@Value("#{systemProperties['java.home']}") |
執行 SpEL 表達式,例如讀取系統屬性。 |