Spring中@PropertySource的使用方法和運(yùn)行原理詳解
1 @PropertySource使用方法和運(yùn)行原理機(jī)制詳解
1.1 @PropertySource簡(jiǎn)要說明
PropertySource注解可以方便和靈活的向Spring的環(huán)境容器(org.springframework.core.env.Environment Environment)中注入一些屬性,這些屬性可以在Bean中使用。
1.2 @PropertySource基本使用
首先,我們用idea構(gòu)建一個(gè)Springboot項(xiàng)目,項(xiàng)目的pom.xml具體內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.11.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>springboot-code</artifactId> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> </project>
項(xiàng)目構(gòu)建完畢后,我們打開@PropertySource類,可以看到其基本內(nèi)容如下:
package org.springframework.context.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.io.support.PropertySourceFactory; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented /** * @PropertySource可以在PropertySources中復(fù)用 **/ @Repeatable(PropertySources.class) public @interface PropertySource { /** * 該P(yáng)ropertySource的名字,如果不指定會(huì)用文件名按照一定的方式生成 **/ String name() default ""; /** * 指定掃描文件的路徑,可以配置多個(gè) **/ String[] value(); /** * 是否忽略文件,如果文件沒有找到,默認(rèn)否,就是如果沒有文件會(huì)報(bào)錯(cuò) **/ boolean ignoreResourceNotFound() default false; /** * 指定文件編碼 **/ String encoding() default ""; /** * 指定文件解析工廠 **/ Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class; }
1.2.1 讀取.properties結(jié)尾的配置文件
首先在resources資源目錄下面建一個(gè)文件名為women.properties的文件,文件的具體內(nèi)容如下:
women.name=lili women.age=18
接著我們編寫一個(gè)Women的類,并在上面通過@PropertySource的方式加載women.properties文件,同時(shí)通過@Value注入的方式把配置文件中的值注入到Women類的相關(guān)屬性上面,最終在Women類上加上@Component注解交給容器管理。相關(guān)代碼的內(nèi)容如下:
package com.dream21th.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource(value = "classpath:women.properties") public class Women { @Value("${women.name}") private String name; @Value("${women.age}") private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } @Override public String toString() { return "Women{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}'; } }
1.2.2 讀取.xml結(jié)尾的配置文件
首先在resources資源目錄下面建一個(gè)文件名為men.properties的文件,文件的具體內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <entry key="man.name">李白</entry> <entry key="man.hobby">寫詩</entry> </properties>
接著我們編寫一個(gè)Men的類,并在上面通過@PropertySource的方式加載men.xml文件,同時(shí)通過@Value注入的方式把配置文件中的值注入到Men類的相關(guān)屬性上面,最終在Men類上加上@Component注解交給容器管理。相關(guān)代碼的內(nèi)容如下:
package com.dream21th.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource(value = "classpath:men.xml") public class Men { @Value("${man.name}") private String name; @Value("${man.hobby}") private String hobby; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } @Override public String toString() { return "Men{" + "name='" + name + '\'' + ", hobby='" + hobby + '\'' + '}'; } }
1.2.3 讀取.yml或者.yaml結(jié)尾的配置文件
由于默認(rèn)的DefaultPropertySourceFactory資源工廠并不能解析.yml或者.yaml結(jié)尾的配置文件。在此,我們先要自定義一個(gè)YAML解析的資源工廠,具體代碼實(shí)現(xiàn)如下:
package com.dream21th.config; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.env.PropertiesPropertySource; import org.springframework.core.env.PropertySource; import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.PropertySourceFactory; import java.io.IOException; import java.util.Properties; public class YAMLPropertySourceFactory implements PropertySourceFactory { @Override public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException { //1,創(chuàng)建一個(gè)YAML文件的解析工廠。 YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); //2,設(shè)置資源。 factory.setResources(encodedResource.getResource()); //3,獲取解析后的Properties對(duì)象 Properties properties = factory.getObject(); //4返回。此時(shí)不能像默認(rèn)工廠那樣返回ResourcePropertySource對(duì)象 ,要返回他的父類PropertiesPropertySource對(duì)象。 return name != null ? new PropertiesPropertySource(name, properties) : new PropertiesPropertySource(encodedResource.getResource().getFilename(),properties); } }
接著在resources資源目錄下面建一個(gè)文件名為person.yml的文件,文件的具體內(nèi)容如下:
person: name: zhangsan address: shanghaihsi
接著我們編寫一個(gè)Person的類,并在上面通過@PropertySource的方式加載person.yml文件,在配置文件的同時(shí)要指定資源文件的解析工廠類,這里我們指定為YAMLPropertySourceFactory.class。同時(shí)通過@Value注入的方式把配置文件中的值注入到Person類的相關(guān)屬性上面,最終在Person類上加上@Component注解交給容器管理。相關(guān)代碼的內(nèi)容如下:
package com.dream21th.service; import com.dream21th.config.YAMLPropertySourceFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource(value = "classpath:person.yml",factory = YAMLPropertySourceFactory.class) public class Person { @Value("${person.name}") private String name; @Value("${person.address}") private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", address='" + address + '}'; } }
1.2.4 讀取其他方式結(jié)尾的配置文件
嚴(yán)格意義上面來講,默認(rèn)的DefaultPropertySourceFactory屬性讀取工廠,不僅僅只能讀取.properties和xml結(jié)尾的配置文件?;疽饬x上能讀取所有后綴的文件,只要文件里面的內(nèi)容按照.properties的方式拼寫。
我們?cè)趓esources資源目錄下面建一個(gè)文件名為other.xxx(xxx代表任意文件的后綴)的文件,文件的具體內(nèi)容如下:
other.name=異類 other.home=火星
接著我們編寫一個(gè)Other的類,并在上面通過@PropertySource的方式加載other.xxx文件,在配置文件的同時(shí)要指定資源文件的編碼,這里我們指定為utf-8。同時(shí)通過@Value注入的方式把配置文件中的值注入到Person類的相關(guān)屬性上面,最終在Other類上加上@Component注解交給容器管理。相關(guān)代碼的內(nèi)容如下:
package com.dream21th.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @PropertySource(value = "classpath:other.xxx",encoding = "utf-8") public class Other { @Value("${other.name}") private String name; @Value("${other.home}") private String home; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHome() { return home; } public void setHome(String home) { this.home = home; } @Override public String toString() { return "Other{" + "name='" + name + '\'' + ", home='" + home + '\'' + '}'; } }
1.2.5 測(cè)試
在啟動(dòng)類下面注入上面例子中的Bean,在容器啟動(dòng)成功后打印相關(guān)數(shù)據(jù),具體實(shí)現(xiàn)代碼如下:
package com.dream21th; import com.dream21th.service.Men; import com.dream21th.service.Other; import com.dream21th.service.Person; import com.dream21th.service.Women; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringbootSourceApplication implements CommandLineRunner { @Autowired private Person person; @Autowired private Women women; @Autowired private Men men; @Autowired private Other other; public static void main(String[] args) { SpringApplication.run(SpringbootSourceApplication.class,args); } @Override public void run(String... args) throws Exception { System.out.println(person); System.out.println(women); System.out.println(men); System.out.println(other); } }
啟動(dòng)成功后,控制臺(tái)的輸出內(nèi)容如下,代表我們通過四種文件類型的屬性讀取成功。
Person{name='zhangsan', address='shanghaihsi} Women{name='lili', age='18'} Men{name='李白', hobby='寫詩'} Other{name='異類', home='火星'}
1.3 @PropertySource使用小結(jié)
通過上面的例子,我們可以讀取不同后綴的文件中的內(nèi)容到容器中去。同時(shí)也要說明,雖然上面我們是一個(gè)配置文件一個(gè)類,并且也是在對(duì)應(yīng)類里面使用對(duì)應(yīng)屬性。但并不代表只能這樣用,@PropertySource屬性中的value屬性是可以輸入多個(gè)路徑的,我們可以在一個(gè)文件中加載所有的文件,然后再其他的實(shí)例中使用相關(guān)的屬性。當(dāng)然也可以在@PropertySources中使用多個(gè)@PropertySource的方式來讀取文件。
1.4 @PropertySource運(yùn)行原理機(jī)制
源碼的啟動(dòng)過程比較復(fù)雜和繁瑣,由于篇幅有限,在這里,我們就不追代碼,直接定位到對(duì)應(yīng)的處理邏輯代碼上。
我們定位到org.springframework.context.annotation.ConfigurationClassParser類的doProcessConfigurationClass方法上面來。
@Nullable protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException { //此處省略一些代碼 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( /** * 類上面有PropertySources.class **/ sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { //處理邏輯 processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } //此處省略一些代碼 }
private void processPropertySource(AnnotationAttributes propertySource) throws IOException { //拿到注解中的名字 String name = propertySource.getString("name"); if (!StringUtils.hasLength(name)) { name = null; } //拿到注解中的編碼 String encoding = propertySource.getString("encoding"); if (!StringUtils.hasLength(encoding)) { encoding = null; } //拿到注解中的文件路徑 String[] locations = propertySource.getStringArray("value"); Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required"); //拿到注解中的是否忽略 boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound"); Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory"); //文件解析工廠 PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ? DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass)); for (String location : locations) { try { String resolvedLocation = this.environment.resolveRequiredPlaceholders(location); Resource resource = this.resourceLoader.getResource(resolvedLocation); //實(shí)際處理邏輯 addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding))); } catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) { // Placeholders not resolvable or resource not found when trying to open it if (ignoreResourceNotFound) { if (logger.isInfoEnabled()) { logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage()); } } else { throw ex; } } } }
//加到Environment中 private void addPropertySource(PropertySource<?> propertySource) { String name = propertySource.getName(); MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources(); if (this.propertySourceNames.contains(name)) { // We've already added a version, we need to extend it PropertySource<?> existing = propertySources.get(name); if (existing != null) { PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ? ((ResourcePropertySource) propertySource).withResourceName() : propertySource); if (existing instanceof CompositePropertySource) { ((CompositePropertySource) existing).addFirstPropertySource(newSource); } else { if (existing instanceof ResourcePropertySource) { existing = ((ResourcePropertySource) existing).withResourceName(); } CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(newSource); composite.addPropertySource(existing); propertySources.replace(name, composite); } return; } } if (this.propertySourceNames.isEmpty()) { propertySources.addLast(propertySource); } else { String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1); propertySources.addBefore(firstProcessed, propertySource); } this.propertySourceNames.add(name); }
到此這篇關(guān)于Spring中@PropertySource的使用方法和運(yùn)行原理詳解的文章就介紹到這了,更多相關(guān)@PropertySource的使用方法和運(yùn)行原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot自動(dòng)裝配之@ComponentScan使用方式
@componentScan注解用于掃描指定路徑下的組件,并自動(dòng)將它們注冊(cè)為Spring?Bean,該注解支持多種過濾規(guī)則,可以自定義掃描過濾規(guī)則,Spring?Boot通過ConfigurationClassPostProcessor處理@ComponentScan注解,并在啟動(dòng)時(shí)創(chuàng)建和注冊(cè)BeanDefinition對(duì)象2025-01-01SpringBoot項(xiàng)目如何把接口參數(shù)中的空白值替換為null值(推薦)
這篇文章主要介紹了SpringBoot項(xiàng)目如何把接口參數(shù)中的空白值替換為null值(推薦),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01SpringBoot加載配置文件的實(shí)現(xiàn)方式總結(jié)
在實(shí)際的項(xiàng)目開發(fā)過程中,我們經(jīng)常需要將某些變量從代碼里面抽離出來,放在配置文件里面,以便更加統(tǒng)一、靈活的管理服務(wù)配置信息。所以本文將為大家總結(jié)一下SpringBoot加載配置文件的常用方式,需要的可以參考一下2022-03-03SpringBoot?Web開發(fā)之請(qǐng)求響應(yīng)、分層解耦問題記錄
在?Spring?Boot?的?Web?請(qǐng)求響應(yīng)處理中,Servlet?起著關(guān)鍵的作用,Servlet?是?Java?Web?開發(fā)中的基本組件,主要負(fù)責(zé)處理客戶端的請(qǐng)求并生成響應(yīng),這篇文章主要介紹了SpringBoot?Web開發(fā)之請(qǐng)求響應(yīng),分層解耦,需要的朋友可以參考下2024-08-08Spring?Boot和Vue前后端分離項(xiàng)目架構(gòu)的全過程
前后端分離是目前互聯(lián)網(wǎng)開發(fā)中比較廣泛使用的開發(fā)模式,主要是將前端和后端的項(xiàng)目業(yè)務(wù)進(jìn)行分離,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot和Vue前后端分離項(xiàng)目架構(gòu)的相關(guān)資料,需要的朋友可以參考下2022-04-04