@Value,@ConfigurationProperties,@EnableConfigurationProperties,@PropertySource,@PropertySources
@Value 注解可以用來將外部的值動態注入到 Bean 中,在 @Value 注解中,可以使用 ${} 或 #{}。${} 與 #{} 的區別如下:
(1)@Value("${}"):可以獲取對應屬性文件中定義的屬性值。
(2)@Value("#{}"):表示 SpEl 表達式通常用來獲取 bean 的屬性,或者調用 bean 的某個方法。
@Value 注解的常用使用方式如下:
注入普通字符串
屬性文件內容如下:
| 1 | str1=hello?world |
Java代碼如下:
| 1 2 3 4 5 6 7 | //?直接將字符串賦值給?str?屬性 @Value("hello?world") private?String?str; //?從屬性文件中獲取值 @Value("${str1}") private?String?str1;?//?結果:hello?world |
如果在屬性文件中沒有定義 str1 屬性名,則 @Value 會拋出如下錯誤“java.lang.IllegalArgumentException: Could not resolve placeholder 'str1' in value "${str1}"”。我們可以在 str1 屬性不存在時,指定默認值,即使沒有定義 str2 也不會拋出錯誤,而是返回默認值。如下:
| 1 2 | @Value("${str2:defaultValue}") private?String?str2;?//?結果:defaultValue |
注入操作系統屬性
可以利用 @Value 注入操作系統屬性。這里我們不能使用“${}”去獲取操作系統屬性。如下:
| 1 2 | @Value("${systemProperties['os.name']}") private?String?osName; |
上面代碼將拋出 java.lang.IllegalArgumentException: Could not resolve placeholder 'systemProperties['os.name']' in value "${systemProperties['os.name']}" 錯誤信息。你需要將 ${} 改為 #{},如下:
| 1 2 | @Value("#{systemProperties['os.name']}") private?String?osName;?//?結果:Windows?10 |
注入表達式結果
在 @Value 中,允許我們使用表達式,然后自動計算表達式的結果。將結果復制給指定的變量。如下:
| 1 2 3 4 5 6 7 | //?生成一個隨機數 @Value("#{?T(java.lang.Math).random()?*?1000.0?}") private?double?randomNumber; //?使用?System?類獲取系統環境變量?PATH @Value("#{?T(java.lang.System).getenv('path')?}") private?String?path; |
注入其他Bean屬性
在 @Value 注解中,也可以將其他 bean 的屬性值注入到當前 bean。如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //?其他Bean @Component public?class?OtherBean?{ ????@Value("OtherBean的NAME屬性") ????private?String?name; ????public?String?getName()?{ ????????return?name; ????} ????public?void?setName(String?name)?{ ????????this.name?=?name; ????} } //?用法 @Component public?class?MyBean?{ ????@Value("#{otherBean.name}") ????private?String?fromAnotherBean; ????//?... } |
注意,其他 bean 使用?@Component 時,如果沒有指定名稱,則默認為類名首字母小寫,如:otherBean 。當然我們也可以使用?@Component("myName") 形式,指定其他 bean 的名稱,此時,訪問則需要使用 @Value("#{myName.name}")。如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //?其他bean,自定義名稱為?myBeans @Component("myBeans") public?class?OtherBean2?{ ????@Value("OtherBean的NAME屬性") ????private?String?name; ????public?String?getName()?{ ????????return?name; ????} ????public?void?setName(String?name)?{ ????????this.name?=?name; ????} } //?用法 @Component public?class?MyBean?{ ????@Value("#{myBeans.name}") ????private?String?fromAnotherBean2; ????//?... } |
注入資源
在 @Value 注解中,也可以用來注入資源(Resource),如:文件資源、URL網絡資源等。如下:
| 1 2 3 4 5 6 7 | //?注入文件資源 @Value("classpath:props/application.properties") private?Resource?fileResource; //?注入URL資源 @Value("https://www.hxstrive.com") private?Resource?urlResource; |
完整代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | @Service public?class?ValueDemo5?{ ????//?注入文件資源 ????@Value("classpath:props/application.properties") ????private?Resource?fileResource; ????//?注入URL資源 ????@Value("https://www.hxstrive.com") ????private?Resource?urlResource; ????public?static?void?main(String[]?args)?throws?Exception?{ ????????ApplicationContext?context?=?new?ClassPathXmlApplicationContext( ????????????????"applicationContent-value.xml"); ????????ValueDemo5?demo?=?context.getBean(ValueDemo5.class); ????????//?輸出文件資源內容 ????????String?fileContent?=?FileUtils.readFileToString( ????????????????demo.getFileResource().getFile(),?"UTF-8"); ????????System.out.println(fileContent); ????????//?輸出URL資源內容 ????????ByteArrayOutputStream?outputStream?=?new?ByteArrayOutputStream(); ????????BufferedInputStream?inputStream?=?new?BufferedInputStream( ????????????????demo.getUrlResource().getInputStream()); ????????byte[]?buffer?=?new?byte[2048]; ????????int?len; ????????while((len?=?inputStream.read(buffer))?!=?-1)?{ ????????????outputStream.write(buffer,?0,?len); ????????} ????????System.out.println(new?String(outputStream.toByteArray(),?"UTF-8")); ????} ????public?Resource?getFileResource()?{ ????????return?fileResource; ????} ????public?void?setFileResource(Resource?fileResource)?{ ????????this.fileResource?=?fileResource; ????} ????public?Resource?getUrlResource()?{ ????????return?urlResource; ????} ????public?void?setUrlResource(Resource?urlResource)?{ ????????this.urlResource?=?urlResource; ????} } |
@Value注入static屬性
一般@Value是使用在非靜態方法上的,對于靜態方法,以下做法是無效的:
| 1 2 | @Value("${myProp}") public?static?String?myProp; |
如果要向靜態屬性注入值,可以使用set方法注入,如下:
| 1 2 3 4 5 6 7 | private?static?String?str1; @Value("${str1}") public?void?setStr1(String?str1)?{ ????System.out.println("setStr1?===>?"?+?str1); ????ValueDemo7.str1?=?str1; } |
如果將 setStr1() 方法設置成 static,則 Spring 將拋出如下告警信息:
13:26:34 WARN [org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor] - Autowired annotation is not supported on static methods: public static void com.huangx.spring4.value.ValueDemo7.setStr1(java.lang.String)
引用外部屬性文件
通過 @Value 將外部配置文件的值動態注入到Bean中。配置文件主要有兩類:
application.properties
在 spring boot 啟動時默認加載此文件 application.properties,spring 則需要我們配置:
| 1 2 3 4 5 6 7 8 9 10 11 12 | <bean?class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> ????<!--?設置如果沒有找到匹配的系統屬性,是否搜索匹配的系統環境變量?--> ????<property?name="searchSystemEnvironment"?value="true"?/> ????<!--?設置如何檢查系統屬性(SYSTEM_PROPERTIES_MODE_FALLBACK=1)?--> ????<property?name="systemPropertiesMode"?value="1"/> ????<!--?加載屬性文件,指定屬性文件地址,可以指定多個?--> ????<property?name="locations"> ????????<list> ????????????<value>classpath:props/application.properties</value> ????????</list> ????</property> </bean> |
自定義屬性文件
自定義屬性文件通過 @PropertySource 加載,@PropertySource 可以同時加載多個文件,也可以加載單個文件。如果第一個屬性文件和第二屬性文件存在相同key,則最后一個屬性文件里的key起作用(前面屬性文件的key將被覆蓋)。加載文件的路徑也可以配置變量,如下面實例中 config.properties 屬性文件的 ${env.model}(用來指定開發環境模式,如:dev-開發環境、prod-生產環境、test-測試環境)
@ConfigurationProperties#
Spring源碼中大量使用了ConfigurationProperties注解,比如server.port就是由該注解獲取到的,通過與其他注解配合使用,能夠實現Bean的按需配置。
該注解有一個prefix屬性,通過指定的前綴,綁定配置文件中的配置,該注解可以放在類上,也可以放在方法上
可以從注解說明中看到,當將該注解作用于方法上時,如果想要有效的綁定配置,那么該方法需要有@Bean注解且所屬Class需要有@Configuration注解。
簡單一句話概括就是:Sring的有效運行是通過上下文(Bean容器)中Bean的配合完成的,Bean可以簡單理解成對象,有些對象需要指定字段內容,那么這些內容我們可以通過配置文件進行綁定,然后將此Bean歸還給容器
作用于方法#
比較常見的就是配置讀寫分離的場景。
配置文件內容#
spring.datasource.druid.write.url=jdbc:mysql://localhost:3306/jpa spring.datasource.druid.write.username=root spring.datasource.druid.write.password=1 spring.datasource.druid.write.driver-class-name=com.mysql.jdbc.Driverspring.datasource.druid.read.url=jdbc:mysql://localhost:3306/jpa spring.datasource.druid.read.username=root spring.datasource.druid.read.password=1 spring.datasource.druid.read.driver-class-name=com.mysql.jdbc.Driver @Configuration public class DruidDataSourceConfig {/*** DataSource 配置* @return*/@ConfigurationProperties(prefix = "spring.datasource.druid.read")@Bean(name = "readDruidDataSource")public DataSource readDruidDataSource() {return new DruidDataSource();}/*** DataSource 配置* @return*/@ConfigurationProperties(prefix = "spring.datasource.druid.write")@Bean(name = "writeDruidDataSource")@Primarypublic DataSource writeDruidDataSource() {return new DruidDataSource();} }也許有的人看到這里會比較疑惑,prefix并沒有指定配置的全限定名,那它是怎么進行配置綁定的呢?
相信大家肯定了解@Value注解,它可以通過全限定名進行配置的綁定,這里的ConfigurationProperties其實就類似于使用多個@Value同時綁定,綁定的對象就是DataSource類型的對象,而且是?隱式綁定?的,意味著在配置文件編寫的時候需要與對應類的字段名稱?相同,比如上述spring.datasource.druid.write.url=jdbc:mysql://localhost:3306/jpa?,當然了,你也可以隨便寫個配置,比如?spring.datasource.druid.write.uuu=www.baidu.com,此時你只需要在注解中加上以下參數即可
以上就完成了多個數據源的配置,為讀寫分離做了鋪墊
作用于Class類及其用法#
配置文件內容#
spring.datasource.url=jdbc:mysql://127.0.0.1:8888/test?useUnicode=false&autoReconnect=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.type=com.alibaba.druid.pool.DruidDataSource @ConfigurationProperties(prefix = "spring.datasource") @Component public class DatasourcePro {private String url;private String username;private String password;// 配置文件中是driver-class-name, 轉駝峰命名便可以綁定成private String driverClassName;private String type;public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getDriverClassName() {return driverClassName;}public void setDriverClassName(String driverClassName) {this.driverClassName = driverClassName;}public String getType() {return type;}public void setType(String type) {this.type = type;} } @Controller @RequestMapping(value = "/config") public class ConfigurationPropertiesController {@Autowiredprivate DatasourcePro datasourcePro;@RequestMapping("/test")@ResponseBodypublic Map<String, Object> test(){Map<String, Object> map = new HashMap<>();map.put("url", datasourcePro.getUrl());map.put("userName", datasourcePro.getUsername());map.put("password", datasourcePro.getPassword());map.put("className", datasourcePro.getDriverClassName());map.put("type", datasourcePro.getType());return map;} }@EnableConfigurationProperties
先說作用:
@EnableConfigurationProperties注解的作用是:使使用 @ConfigurationProperties 注解的類生效。
說明:
如果一個配置類只配置@ConfigurationProperties注解,而沒有使用@Component,那么在IOC容器中是獲取不到properties 配置文件轉化的bean。說白了 @EnableConfigurationProperties 相當于把使用 @ConfigurationProperties 的類進行了一次注入。
測試發現 @ConfigurationProperties 與 @EnableConfigurationProperties 關系特別大。
測試證明:
@ConfigurationProperties 與 @EnableConfigurationProperties 的關系。
@EnableConfigurationProperties 文檔中解釋:
當@EnableConfigurationProperties注解應用到你的@Configuration時, 任何被@ConfigurationProperties注解的beans將自動被Environment屬性配置。 這種風格的配置特別適合與SpringApplication的外部YAML配置進行配合使用。
測試發現:
1.使用 @EnableConfigurationProperties 進行注冊
自動配置設置
service.properties.name=my-test-name service.properties.ip=192.168.1.1 service.user=kayle service.port=8080一切正常,但是 HelloServiceAutoConfiguration 頭部不使用 @EnableConfigurationProperties,測訪問報錯。
2.不使用 @EnableConfigurationProperties 進行注冊,使用 @Component 注冊
@ConfigurationProperties(prefix = "service.properties") @Component public class HelloServiceProperties {private static final String SERVICE_NAME = "test-service";private String msg = SERVICE_NAME;public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}}Controller 不變,一切正常,如果注釋掉 @Component 測啟動報錯。
由此證明,兩種方式都是將被 @ConfigurationProperties 修飾的類,加載到 Spring Env 中。
簡單介紹一下@PropertySource和@PropertySources這倆注解
@PropertySource注解概述
@PropertySource注解是Spring 3.1開始引入的配置類注解。通過@PropertySource注解可以將properties配置文件中的key/value存儲到Spring的Environment中,Environment接口提供了方法去讀取配置文件中的值,參數是properties配置文件中定義的key值。當然了,也可以使用@Value注解用${}占位符為bean的屬性注入值。
我們來看一下@PropertySource注解的源代碼,如下所示。
從@PropertySource的源碼中可以看出,我們可以通過@PropertySource注解指定多個properties文件,使用的形式如下所示。
@PropertySource(value={"classpath:/person.properties", "classpath:/car.properties"})
1
細心的讀者可以看到,在@PropertySource注解的上面標注了如下的注解信息。
@Repeatable(PropertySources.class)
1
看到這里,小伙伴們是不是有種恍然大悟的感覺呢?沒錯,我們也可以使用@PropertySources注解來指定properties配置文件。
@PropertySources注解概述
首先,我們也來看下@PropertySources注解的源碼,如下所示。
@PropertySources注解的源碼比較簡單,只有一個PropertySource[]數組類型的value屬性,那我們如何使用@PropertySources注解指定配置文件呢?其實也很簡單,使用如下所示的方式就可以了。
@PropertySources(value={
? ? @PropertySource(value={"classpath:/person.properties"}),
? ? @PropertySource(value={"classpath:/car.properties"}),
})
是不是很簡單呢?接下來,我們就以一個小案例來說明@PropertySource注解的用法。
一個小案例來說明@PropertySource注解的用法
準備工作
首先,我們在工程的src/main/resources目錄下創建一個配置文件,例如person.properties,該文件的內容如下所示。
person.nickName=小甜甜
1
然后,我們在Person類中新增一個nickName字段,如下所示。
?
目前,我們并沒有為Person類的nickName字段賦值,所以,此時Person類的nickName字段的值為空。我們可以運行IOCTest_PropertyValue類中的test01()方法來看下輸出結果,如下所示。
可以看到,Person類的nickName字段的值確實輸出了null。
使用注解方式獲取值
如果我們使用注解的方式,那么該如何做呢?首先,我們需要在MainConfigOfPropertyValues配置類上添加一個@PropertySource注解,如下所示。
?
package com.meimeixia.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource;import com.meimeixia.bean.Person;// 使用@PropertySource讀取外部配置文件中的key/value保存到運行的環境變量中,加載完外部的配置文件以后,使用${}取出配置文件中的值 @PropertySource(value={"classpath:/person.properties"}) @Configuration public class MainConfigOfPropertyValues {@Beanpublic Person person() {return new Person();}}?
這里使用的@PropertySource(value={"classpath:/person.properties"})注解就相當于XML配置文件中使用的<context:property-placeholder location="classpath:person.properties" />。
然后,我們就可以在Person類的nickName字段上使用@Value注解來獲取person.properties文件中的值了,如下所示。
@Value("${person.nickName}")
private String nickName; // 昵稱
配置完成后,我們再次運行IOCTest_PropertyValue類中的test01()方法來看下輸出結果,如下所示。
可以看到,此時Person類的nickName字段已經注入小甜甜這個值了。
使用Environment獲取值
上面我已經說過,使用@PropertySource注解讀取外部配置文件中的key/value之后,是將其保存到運行的環境變量中了,所以我們也可以通過運行環境來獲取外部配置文件中的值。
這里,我們可以稍微修改一下IOCTest_PropertyValue類中的test01()方法,即在其中添加一段使用Environment獲取person.properties文件中的值的代碼,如下所示。
@Test public void test01() {printBeans(applicationContext);System.out.println("===================");Person person = (Person) applicationContext.getBean("person");System.out.println(person);ConfigurableEnvironment environment = applicationContext.getEnvironment();String property = environment.getProperty("person.nickName");System.out.println(property);// 關閉容器applicationContext.close(); }?運行以上test01()方法,可以看到輸出的結果信息如下所示。
可以看到,使用Environment確實能夠獲取到person.properties文件中的值。
?
總結
以上是生活随笔為你收集整理的@Value,@ConfigurationProperties,@EnableConfigurationProperties,@PropertySource,@PropertySources的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring框架【尚硅谷】
- 下一篇: java棋盘最短路径障碍物_(Eucle