寫在前面
在之前的文章中,我們探討了如何向Spring的IOC容器中註冊bean元件,講解了有關bean元件的生命週期的知識。今天,我們就來一起聊聊@Value註解的用法。
專案工程原始碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
@Value註解
Spring中的@Value註解可以為bean中的屬性賦值。我們先來看看@Value註解的原始碼,如下所示。
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
String value();
}
從@Value註解的原始碼,我們可以看出:@Value註解可以標註在欄位、方法、引數、註解上,在程式執行期間生效。
@Value註解用法
1.不通過配置檔案注入屬性的情況
通過@Value將外部的值動態注入到Bean中,使用的情況有:
- 注入普通字串
@Value("normal")
private String normal; // 注入普通字串
- 注入作業系統屬性
@Value("#{systemProperties['os.name']}")
private String systemPropertiesName; // 注入作業系統屬性
- 注入表示式結果
@Value("#{ T(java.lang.Math).random() * 100.0 }")
private double randomNumber; //注入表示式結果
- 注入其他Bean屬性
@Value("#{person.name}")
private String name; // 注入其他Bean屬性:注入person物件的屬性name
- 注入檔案資源
@Value("classpath:io/mykit/spring/config/config.properties")
private Resource resourceFile; // 注入檔案資源
- 注入URL資源
@Value("http://www.baidu.com")
private Resource url; // 注入URL資源
2.通過配置檔案注入屬性的情況
通過@Value(“${app.name}”)語法將屬性檔案的值注入到bean的屬性中,如下所示。
@Component
// 引入外部配置檔案組:${app.configinject}的值來自config.properties。
// 如果相同
@PropertySource({"classpath:io/mykit/spring/config/config.properties",
"classpath:io/mykit/spring/config/config_${anotherfile.configinject}.properties"})
public class ConfigurationFileInject{
// 這裡的值來自application.properties,spring boot啟動時預設載入此檔案
@Value("${app.name}")
private String appName;
// 注入第一個配置外部檔案屬性
@Value("${book.name}")
private String bookName;
// 注入第二個配置外部檔案屬性
@Value("${book.name.placeholder}")
private String bookNamePlaceholder;
// 注入環境變數物件,儲存注入的屬性值
@Autowired
private Environment env;
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append("bookName=").append(bookName).append("\r\n")
.append("bookNamePlaceholder=").append(bookNamePlaceholder).append("\r\n")
.append("appName=").append(appName).append("\r\n")
.append("env=").append(env).append("\r\n")
// 從eniroment中獲取屬性值
.append("env=").append(env.getProperty("book.name.placeholder")).append("\r\n");
return sb.toString();
}
}
3.@Value中#{..}和${...}的區別
我們這裡提供一個測試屬性檔案:advance_value_inject.properties,大致的內容如下所示。
server.name=server1,server2,server3
author.name=binghe
測試類AdvanceValueInject:引入advance_value_inject.properties檔案,作為屬性的注入
@Component
@PropertySource({"classpath:io/mykit/spring/config/advance_value_inject.properties"})
public class AdvanceValueInject {
...
}
${...}的用法
{}裡面的內容必須符合SpEL表示式, 通過@Value(“${spelDefault.value}”)可以獲取屬性檔案中對應的值,但是如果屬性檔案中沒有這個屬性,則會報錯。可以通過賦予預設值解決這個問題,如下所示。
@Value("${author.name:binghe}")
上述程式碼的含義表示向bean的屬性中注入配置檔案中的author.name屬性的值,如果配置檔案中沒有author.name屬性,則向bean的屬性中注入預設值binghe。例如下面的程式碼片段。
@Value("${author.name:binghe}")
private String name;
#{…}的用法
// SpEL:呼叫字串Hello World的concat方法
@Value("#{'Hello World'.concat('!')}")
private String helloWorld;
// SpEL: 呼叫字串的getBytes方法,然後呼叫length屬性
@Value("#{'Hello World'.bytes.length}")
private String helloWorldbytes;
${…}和#{…}混合使用
${...}和#{...}可以混合使用,如下文程式碼執行順序:通過${server.name}從屬性檔案中獲取值並進行替換,然後就變成了 執行SpEL表示式{‘server1,server2,server3’.split(‘,’)}。
// SpEL: 傳入一個字串,根據","切分後插入列表中, #{}和${}配置使用(注意單引號,注意不能反過來${}在外面,#{}在裡面)
@Value("#{'${server.name}'.split(',')}")
private List<String> servers;
在上文中#{}在外面,${}在裡面可以執行成功,那麼反過來是否可以呢?也就是說能否讓${}在外面,#{}在裡面,如下程式碼所示。
// SpEL: 注意不能反過來${}在外面,#{}在裡面,這個會執行失敗
@Value("${#{'HelloWorld'.concat('_')}}")
private List<String> servers2;
答案是不能。因為Spring執行${}時機要早於#{},當Spring執行外層的${}時,內部的#{}為空,所以會執行失敗!
@Value註解用法小結:
#{…} 用於執行SpEl表示式,並將內容賦值給屬性。
${…} 主要用於載入外部屬性檔案中的值。
#{…} 和${…} 可以混合使用,但是必須#{}外面,${}在裡面。
@Value註解案例
這裡,我們還是以一個小案例的形式來說明。
首先,我們來建立一個Person類作為測試的bean元件,如下所示。
package io.mykit.spring.plugins.register.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
/**
* @author binghe
* @version 1.0.0
* @description 測試實體類
*/
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {
private static final long serialVersionUID = 7387479910468805194L;
private String name;
private Integer age;
}
接下來,建立一個新的配置類PropertyValueConfig,用來配置Spring的bean元件,我們在PropertyValueConfig類中將Person類的物件註冊到IOC容器中,如下所示。
package io.mykit.spring.plugins.register.config;
import io.mykit.spring.plugins.register.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author binghe
* @version 1.0.0
* @description 測試屬性賦值
*/
@Configuration
public class PropertyValueConfig {
@Bean
public Person person(){
return new Person();
}
}
我們再來建立一個測試類PropertyValueTest,在PropertyValueTest類中建立測試方法testPropertyValue01(),並在testPropertyValue01()方法中通過PropertyValueConfig類建立AnnotationConfigApplicationContext物件,列印出目前IOC容器中存在的bean名稱,如下所示。
package io.mykit.spring.test;
import io.mykit.spring.plugins.register.config.PropertyValueConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Arrays;
/**
* @author binghe
* @version 1.0.0
* @description 測試bean的生命週期
*/
public class PropertyValueTest {
@Test
public void testPropertyValue01(){
//建立IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PropertyValueConfig.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
}
}
此時,我們執行PropertyValueTest類的testPropertyValue01()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
propertyValueConfig
person
從輸出的結果資訊中,可以看出,IOC容器中除了Spring框架註冊的bean之外,還包含我們自己向IOC容器中註冊的bean元件:propertyValueConfig和person。
接下來,我們改造下PropertyValueTest類的testPropertyValue01()方法,輸出Person物件的資訊,如下所示。
package io.mykit.spring.test;
import io.mykit.spring.plugins.register.bean.Person;
import io.mykit.spring.plugins.register.config.PropertyValueConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Arrays;
/**
* @author binghe
* @version 1.0.0
* @description 測試bean的生命週期
*/
public class PropertyValueTest {
@Test
public void testPropertyValue01(){
//建立IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PropertyValueConfig.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
System.out.println("================================");
Person person = (Person) context.getBean("person");
System.out.println(person);
}
}
接下來,再次執行PropertyValueTest類的testPropertyValue01()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
propertyValueConfig
person
================================
Person(name=null, age=null)
可以看到,向IOC容器中註冊的Person物件的name屬性為null,age屬性為null。那如何向Person物件的name屬性和age屬性賦值呢?此時,Spring中的@Value註解就派上了用場。
如果我們通過XML檔案為bean的屬性賦值,則可以通過如下配置的方式實現。
<bean id = "person" class="io.mykit.spring.plugins.register.bean.Person">
<property name="name" value="binghe"></property>
<property name="age" value="18"></property>
</bean>
如果使用註解該如何實現呢?別急,往下看!
我們可以在Person類的屬性上使用@Value註解為屬性賦值,如下所示。
package io.mykit.spring.plugins.register.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import java.io.Serializable;
/**
* @author binghe
* @version 1.0.0
* @description 測試實體類
*/
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Person implements Serializable {
private static final long serialVersionUID = 7387479910468805194L;
@Value("binghe")
private String name;
@Value("#{20-2}")
private Integer age;
}
此時,我們再次執行PropertyValueTest類的testPropertyValue01()方法,輸出的結果資訊如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
propertyValueConfig
person
================================
Person(name=binghe, age=18)
可以看到,使用@Value註解已經向Person物件的name屬性中注入了binghe,向age屬性中注入了18。
好了,我們們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!
專案工程原始碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
寫在最後
如果覺得文章對你有點幫助,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回覆“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。
部分參考:blog.csdn.net/hry2015/article/details/72453920