Spring中@PropertySource的使用方法和運行原理詳解
1 @PropertySource使用方法和運行原理機制詳解
1.1 @PropertySource簡要說明
PropertySource注解可以方便和靈活的向Spring的環(huán)境容器(org.springframework.core.env.Environment Environment)中注入一些屬性,這些屬性可以在Bean中使用。
1.2 @PropertySource基本使用
首先,我們用idea構(gòu)建一個Springboot項目,項目的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>項目構(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中復用
**/
@Repeatable(PropertySources.class)
public @interface PropertySource {
/**
* 該PropertySource的名字,如果不指定會用文件名按照一定的方式生成
**/
String name() default "";
/**
* 指定掃描文件的路徑,可以配置多個
**/
String[] value();
/**
* 是否忽略文件,如果文件沒有找到,默認否,就是如果沒有文件會報錯
**/
boolean ignoreResourceNotFound() default false;
/**
* 指定文件編碼
**/
String encoding() default "";
/**
* 指定文件解析工廠
**/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}1.2.1 讀取.properties結(jié)尾的配置文件
首先在resources資源目錄下面建一個文件名為women.properties的文件,文件的具體內(nèi)容如下:
women.name=lili women.age=18
接著我們編寫一個Women的類,并在上面通過@PropertySource的方式加載women.properties文件,同時通過@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資源目錄下面建一個文件名為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>接著我們編寫一個Men的類,并在上面通過@PropertySource的方式加載men.xml文件,同時通過@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é)尾的配置文件
由于默認的DefaultPropertySourceFactory資源工廠并不能解析.yml或者.yaml結(jié)尾的配置文件。在此,我們先要自定義一個YAML解析的資源工廠,具體代碼實現(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)建一個YAML文件的解析工廠。
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
//2,設(shè)置資源。
factory.setResources(encodedResource.getResource());
//3,獲取解析后的Properties對象
Properties properties = factory.getObject();
//4返回。此時不能像默認工廠那樣返回ResourcePropertySource對象 ,要返回他的父類PropertiesPropertySource對象。
return name != null ? new PropertiesPropertySource(name, properties) :
new PropertiesPropertySource(encodedResource.getResource().getFilename(),properties);
}
}接著在resources資源目錄下面建一個文件名為person.yml的文件,文件的具體內(nèi)容如下:
person: name: zhangsan address: shanghaihsi
接著我們編寫一個Person的類,并在上面通過@PropertySource的方式加載person.yml文件,在配置文件的同時要指定資源文件的解析工廠類,這里我們指定為YAMLPropertySourceFactory.class。同時通過@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é)尾的配置文件
嚴格意義上面來講,默認的DefaultPropertySourceFactory屬性讀取工廠,不僅僅只能讀取.properties和xml結(jié)尾的配置文件。基本意義上能讀取所有后綴的文件,只要文件里面的內(nèi)容按照.properties的方式拼寫。
我們在resources資源目錄下面建一個文件名為other.xxx(xxx代表任意文件的后綴)的文件,文件的具體內(nèi)容如下:
other.name=異類 other.home=火星
接著我們編寫一個Other的類,并在上面通過@PropertySource的方式加載other.xxx文件,在配置文件的同時要指定資源文件的編碼,這里我們指定為utf-8。同時通過@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 測試
在啟動類下面注入上面例子中的Bean,在容器啟動成功后打印相關(guān)數(shù)據(jù),具體實現(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);
}
}啟動成功后,控制臺的輸出內(nèi)容如下,代表我們通過四種文件類型的屬性讀取成功。
Person{name='zhangsan', address='shanghaihsi}
Women{name='lili', age='18'}
Men{name='李白', hobby='寫詩'}
Other{name='異類', home='火星'}1.3 @PropertySource使用小結(jié)
通過上面的例子,我們可以讀取不同后綴的文件中的內(nèi)容到容器中去。同時也要說明,雖然上面我們是一個配置文件一個類,并且也是在對應類里面使用對應屬性。但并不代表只能這樣用,@PropertySource屬性中的value屬性是可以輸入多個路徑的,我們可以在一個文件中加載所有的文件,然后再其他的實例中使用相關(guān)的屬性。當然也可以在@PropertySources中使用多個@PropertySource的方式來讀取文件。
1.4 @PropertySource運行原理機制
源碼的啟動過程比較復雜和繁瑣,由于篇幅有限,在這里,我們就不追代碼,直接定位到對應的處理邏輯代碼上。
我們定位到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);
//實際處理邏輯
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的使用方法和運行原理詳解的文章就介紹到這了,更多相關(guān)@PropertySource的使用方法和運行原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
jar包在windows后臺運行,通過.bat文件實現(xiàn)
文章主要講解了在Windows后臺運行JAR包的方法,通過創(chuàng)建啟動.bat和停止.bat文件,使用javaw方式運行JAR包,可以在關(guān)閉cmd界面后仍然保持程序運行,提供了啟動.bat和停止.bat的具體內(nèi)容2026-04-04
Java輸入三個整數(shù)并把他們由小到大輸出(x,y,z)
這篇文章主要介紹了輸入三個整數(shù)x,y,z,請把這三個數(shù)由小到大輸出,需要的朋友可以參考下2017-02-02
使用dynamic datasource springboot starter實現(xiàn)多數(shù)據(jù)源及源碼分析
這篇文章主要介紹了使用dynamic-datasource-spring-boot-starter做多數(shù)據(jù)源及源碼分析,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09
基于java.lang.IllegalArgumentException異常報錯問題及解決
這篇文章主要介紹了基于java.lang.IllegalArgumentException異常報錯問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
細數(shù)Java接口的概念、分類及與抽象類的區(qū)別
下面小編就為大家?guī)硪黄殧?shù)Java接口的概念、分類及與抽象類的區(qū)別。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11

