@Value,@ConfigurationProperties,@EnableConfigurationProperties,@PropertySource,@PropertySources
@Value 注解可以用來(lái)將外部的值動(dòng)態(tài)注入到 Bean 中,在 @Value 注解中,可以使用 ${} 或 #{}。${} 與 #{} 的區(qū)別如下:
(1)@Value("${}"):可以獲取對(duì)應(yīng)屬性文件中定義的屬性值。
(2)@Value("#{}"):表示 SpEl 表達(dá)式通常用來(lái)獲取 bean 的屬性,或者調(diào)用 bean 的某個(gè)方法。
@Value 注解的常用使用方式如下:
注入普通字符串
屬性文件內(nèi)容如下:
| 1 | str1=hello?world |
Java代碼如下:
| 1 2 3 4 5 6 7 | //?直接將字符串賦值給?str?屬性 @Value("hello?world") private?String?str; //?從屬性文件中獲取值 @Value("${str1}") private?String?str1;?//?結(jié)果:hello?world |
如果在屬性文件中沒(méi)有定義 str1 屬性名,則 @Value 會(huì)拋出如下錯(cuò)誤“java.lang.IllegalArgumentException: Could not resolve placeholder 'str1' in value "${str1}"”。我們可以在 str1 屬性不存在時(shí),指定默認(rèn)值,即使沒(méi)有定義 str2 也不會(huì)拋出錯(cuò)誤,而是返回默認(rèn)值。如下:
| 1 2 | @Value("${str2:defaultValue}") private?String?str2;?//?結(jié)果:defaultValue |
注入操作系統(tǒng)屬性
可以利用 @Value 注入操作系統(tǒng)屬性。這里我們不能使用“${}”去獲取操作系統(tǒng)屬性。如下:
| 1 2 | @Value("${systemProperties['os.name']}") private?String?osName; |
上面代碼將拋出 java.lang.IllegalArgumentException: Could not resolve placeholder 'systemProperties['os.name']' in value "${systemProperties['os.name']}" 錯(cuò)誤信息。你需要將 ${} 改為 #{},如下:
| 1 2 | @Value("#{systemProperties['os.name']}") private?String?osName;?//?結(jié)果:Windows?10 |
注入表達(dá)式結(jié)果
在 @Value 中,允許我們使用表達(dá)式,然后自動(dòng)計(jì)算表達(dá)式的結(jié)果。將結(jié)果復(fù)制給指定的變量。如下:
| 1 2 3 4 5 6 7 | //?生成一個(gè)隨機(jī)數(shù) @Value("#{?T(java.lang.Math).random()?*?1000.0?}") private?double?randomNumber; //?使用?System?類獲取系統(tǒng)環(huán)境變量?PATH @Value("#{?T(java.lang.System).getenv('path')?}") private?String?path; |
注入其他Bean屬性
在 @Value 注解中,也可以將其他 bean 的屬性值注入到當(dāng)前 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 時(shí),如果沒(méi)有指定名稱,則默認(rèn)為類名首字母小寫(xiě),如:otherBean 。當(dāng)然我們也可以使用?@Component("myName") 形式,指定其他 bean 的名稱,此時(shí),訪問(wèn)則需要使用 @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 注解中,也可以用來(lái)注入資源(Resource),如:文件資源、URL網(wǎng)絡(luò)資源等。如下:
| 1 2 3 4 5 6 7 | //?注入文件資源 @Value("classpath:props/application.properties") private?Resource?fileResource; //?注入U(xiǎn)RL資源 @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; ????//?注入U(xiǎn)RL資源 ????@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); ????????//?輸出文件資源內(nèi)容 ????????String?fileContent?=?FileUtils.readFileToString( ????????????????demo.getFileResource().getFile(),?"UTF-8"); ????????System.out.println(fileContent); ????????//?輸出URL資源內(nèi)容 ????????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是使用在非靜態(tài)方法上的,對(duì)于靜態(tài)方法,以下做法是無(wú)效的:
| 1 2 | @Value("${myProp}") public?static?String?myProp; |
如果要向靜態(tài)屬性注入值,可以使用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() 方法設(shè)置成 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)
引用外部屬性文件
通過(guò) @Value 將外部配置文件的值動(dòng)態(tài)注入到Bean中。配置文件主要有兩類:
application.properties
在 spring boot 啟動(dòng)時(shí)默認(rèn)加載此文件 application.properties,spring 則需要我們配置:
| 1 2 3 4 5 6 7 8 9 10 11 12 | <bean?class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> ????<!--?設(shè)置如果沒(méi)有找到匹配的系統(tǒng)屬性,是否搜索匹配的系統(tǒng)環(huán)境變量?--> ????<property?name="searchSystemEnvironment"?value="true"?/> ????<!--?設(shè)置如何檢查系統(tǒng)屬性(SYSTEM_PROPERTIES_MODE_FALLBACK=1)?--> ????<property?name="systemPropertiesMode"?value="1"/> ????<!--?加載屬性文件,指定屬性文件地址,可以指定多個(gè)?--> ????<property?name="locations"> ????????<list> ????????????<value>classpath:props/application.properties</value> ????????</list> ????</property> </bean> |
自定義屬性文件
自定義屬性文件通過(guò) @PropertySource 加載,@PropertySource 可以同時(shí)加載多個(gè)文件,也可以加載單個(gè)文件。如果第一個(gè)屬性文件和第二屬性文件存在相同key,則最后一個(gè)屬性文件里的key起作用(前面屬性文件的key將被覆蓋)。加載文件的路徑也可以配置變量,如下面實(shí)例中 config.properties 屬性文件的 ${env.model}(用來(lái)指定開(kāi)發(fā)環(huán)境模式,如:dev-開(kāi)發(fā)環(huán)境、prod-生產(chǎn)環(huán)境、test-測(cè)試環(huán)境)
@ConfigurationProperties#
Spring源碼中大量使用了ConfigurationProperties注解,比如server.port就是由該注解獲取到的,通過(guò)與其他注解配合使用,能夠?qū)崿F(xiàn)Bean的按需配置。
該注解有一個(gè)prefix屬性,通過(guò)指定的前綴,綁定配置文件中的配置,該注解可以放在類上,也可以放在方法上
可以從注解說(shuō)明中看到,當(dāng)將該注解作用于方法上時(shí),如果想要有效的綁定配置,那么該方法需要有@Bean注解且所屬Class需要有@Configuration注解。
簡(jiǎn)單一句話概括就是:Sring的有效運(yùn)行是通過(guò)上下文(Bean容器)中Bean的配合完成的,Bean可以簡(jiǎn)單理解成對(duì)象,有些對(duì)象需要指定字段內(nèi)容,那么這些內(nèi)容我們可以通過(guò)配置文件進(jìn)行綁定,然后將此Bean歸還給容器
作用于方法#
比較常見(jiàn)的就是配置讀寫(xiě)分離的場(chǎng)景。
配置文件內(nèi)容#
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();} }也許有的人看到這里會(huì)比較疑惑,prefix并沒(méi)有指定配置的全限定名,那它是怎么進(jìn)行配置綁定的呢?
相信大家肯定了解@Value注解,它可以通過(guò)全限定名進(jìn)行配置的綁定,這里的ConfigurationProperties其實(shí)就類似于使用多個(gè)@Value同時(shí)綁定,綁定的對(duì)象就是DataSource類型的對(duì)象,而且是?隱式綁定?的,意味著在配置文件編寫(xiě)的時(shí)候需要與對(duì)應(yīng)類的字段名稱?相同,比如上述spring.datasource.druid.write.url=jdbc:mysql://localhost:3306/jpa?,當(dāng)然了,你也可以隨便寫(xiě)個(gè)配置,比如?spring.datasource.druid.write.uuu=www.baidu.com,此時(shí)你只需要在注解中加上以下參數(shù)即可
以上就完成了多個(gè)數(shù)據(jù)源的配置,為讀寫(xiě)分離做了鋪墊
作用于Class類及其用法#
配置文件內(nèi)容#
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, 轉(zhuǎn)駝峰命名便可以綁定成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
先說(shuō)作用:
@EnableConfigurationProperties注解的作用是:使使用 @ConfigurationProperties 注解的類生效。
說(shuō)明:
如果一個(gè)配置類只配置@ConfigurationProperties注解,而沒(méi)有使用@Component,那么在IOC容器中是獲取不到properties 配置文件轉(zhuǎn)化的bean。說(shuō)白了 @EnableConfigurationProperties 相當(dāng)于把使用 @ConfigurationProperties 的類進(jìn)行了一次注入。
測(cè)試發(fā)現(xiàn) @ConfigurationProperties 與 @EnableConfigurationProperties 關(guān)系特別大。
測(cè)試證明:
@ConfigurationProperties 與 @EnableConfigurationProperties 的關(guān)系。
@EnableConfigurationProperties 文檔中解釋:
當(dāng)@EnableConfigurationProperties注解應(yīng)用到你的@Configuration時(shí), 任何被@ConfigurationProperties注解的beans將自動(dòng)被Environment屬性配置。 這種風(fēng)格的配置特別適合與SpringApplication的外部YAML配置進(jìn)行配合使用。
測(cè)試發(fā)現(xiàn):
1.使用 @EnableConfigurationProperties 進(jìn)行注冊(cè)
自動(dòng)配置設(shè)置
service.properties.name=my-test-name service.properties.ip=192.168.1.1 service.user=kayle service.port=8080一切正常,但是 HelloServiceAutoConfiguration 頭部不使用 @EnableConfigurationProperties,測(cè)訪問(wèn)報(bào)錯(cuò)。
2.不使用 @EnableConfigurationProperties 進(jìn)行注冊(cè),使用 @Component 注冊(cè)
@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 測(cè)啟動(dòng)報(bào)錯(cuò)。
由此證明,兩種方式都是將被 @ConfigurationProperties 修飾的類,加載到 Spring Env 中。
簡(jiǎn)單介紹一下@PropertySource和@PropertySources這倆注解
@PropertySource注解概述
@PropertySource注解是Spring 3.1開(kāi)始引入的配置類注解。通過(guò)@PropertySource注解可以將properties配置文件中的key/value存儲(chǔ)到Spring的Environment中,Environment接口提供了方法去讀取配置文件中的值,參數(shù)是properties配置文件中定義的key值。當(dāng)然了,也可以使用@Value注解用${}占位符為bean的屬性注入值。
我們來(lái)看一下@PropertySource注解的源代碼,如下所示。
從@PropertySource的源碼中可以看出,我們可以通過(guò)@PropertySource注解指定多個(gè)properties文件,使用的形式如下所示。
@PropertySource(value={"classpath:/person.properties", "classpath:/car.properties"})
1
細(xì)心的讀者可以看到,在@PropertySource注解的上面標(biāo)注了如下的注解信息。
@Repeatable(PropertySources.class)
1
看到這里,小伙伴們是不是有種恍然大悟的感覺(jué)呢?沒(méi)錯(cuò),我們也可以使用@PropertySources注解來(lái)指定properties配置文件。
@PropertySources注解概述
首先,我們也來(lái)看下@PropertySources注解的源碼,如下所示。
@PropertySources注解的源碼比較簡(jiǎn)單,只有一個(gè)PropertySource[]數(shù)組類型的value屬性,那我們?nèi)绾问褂?#64;PropertySources注解指定配置文件呢?其實(shí)也很簡(jiǎn)單,使用如下所示的方式就可以了。
@PropertySources(value={
? ? @PropertySource(value={"classpath:/person.properties"}),
? ? @PropertySource(value={"classpath:/car.properties"}),
})
是不是很簡(jiǎn)單呢?接下來(lái),我們就以一個(gè)小案例來(lái)說(shuō)明@PropertySource注解的用法。
一個(gè)小案例來(lái)說(shuō)明@PropertySource注解的用法
準(zhǔn)備工作
首先,我們?cè)诠こ痰膕rc/main/resources目錄下創(chuàng)建一個(gè)配置文件,例如person.properties,該文件的內(nèi)容如下所示。
person.nickName=小甜甜
1
然后,我們?cè)赑erson類中新增一個(gè)nickName字段,如下所示。
?
目前,我們并沒(méi)有為Person類的nickName字段賦值,所以,此時(shí)Person類的nickName字段的值為空。我們可以運(yùn)行IOCTest_PropertyValue類中的test01()方法來(lái)看下輸出結(jié)果,如下所示。
可以看到,Person類的nickName字段的值確實(shí)輸出了null。
使用注解方式獲取值
如果我們使用注解的方式,那么該如何做呢?首先,我們需要在MainConfigOfPropertyValues配置類上添加一個(gè)@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保存到運(yùn)行的環(huán)境變量中,加載完外部的配置文件以后,使用${}取出配置文件中的值 @PropertySource(value={"classpath:/person.properties"}) @Configuration public class MainConfigOfPropertyValues {@Beanpublic Person person() {return new Person();}}?
這里使用的@PropertySource(value={"classpath:/person.properties"})注解就相當(dāng)于XML配置文件中使用的<context:property-placeholder location="classpath:person.properties" />。
然后,我們就可以在Person類的nickName字段上使用@Value注解來(lái)獲取person.properties文件中的值了,如下所示。
@Value("${person.nickName}")
private String nickName; // 昵稱
配置完成后,我們?cè)俅芜\(yùn)行IOCTest_PropertyValue類中的test01()方法來(lái)看下輸出結(jié)果,如下所示。
可以看到,此時(shí)Person類的nickName字段已經(jīng)注入小甜甜這個(gè)值了。
使用Environment獲取值
上面我已經(jīng)說(shuō)過(guò),使用@PropertySource注解讀取外部配置文件中的key/value之后,是將其保存到運(yùn)行的環(huán)境變量中了,所以我們也可以通過(guò)運(yùn)行環(huán)境來(lái)獲取外部配置文件中的值。
這里,我們可以稍微修改一下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);// 關(guān)閉容器applicationContext.close(); }?運(yùn)行以上test01()方法,可以看到輸出的結(jié)果信息如下所示。
可以看到,使用Environment確實(shí)能夠獲取到person.properties文件中的值。
?
總結(jié)
以上是生活随笔為你收集整理的@Value,@ConfigurationProperties,@EnableConfigurationProperties,@PropertySource,@PropertySources的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring框架【尚硅谷】
- 下一篇: java棋盘最短路径障碍物_(Eucle