偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

深度解析@PropertySource注解

開發(fā) 架構(gòu)
進(jìn)一步學(xué)習(xí)并掌握@PropertySource注解加載配置文件的案例和流程,從源碼級別徹底掌握@PropertySource注解在Spring底層的執(zhí)行流程。

一、學(xué)習(xí)指引

@PropertySource注解是用來干啥的呢?

在日常開發(fā)中,你有沒有遇到過這樣一種場景:項目中需要編寫很多配置文件,將一些系統(tǒng)信息配置化,此時,往往需要編寫專門的工具類或者方法來讀取并解析這些配置文件,將配置文件中的配置項內(nèi)容加載到系統(tǒng)內(nèi)存中。后續(xù)在使用這些配置項時,可以直接通過工具類或者方法獲取加載到內(nèi)存中的配置項。

沒錯,@PropertySource注解就是Spring中提供的一個可以加載配置文件的注解,并且可以將配置文件中的內(nèi)容存放到Spring的環(huán)境變量中。

二、注解說明

簡單介紹下@PropertySource注解吧!

@PropertySource注解是Spring中提供的一個通過指定配置文件位置來加載配置文件的注解,并且可以將配置文件中的內(nèi)容存放到Spring的環(huán)境變量中。除了可以通過Spring的環(huán)境變量讀取配置項之外,還可以通過@Value注解獲取配置項的值。

另外,Spring中還提供了一個@PropertySources注解,在@PropertySources注解注解中,可以引入多個@PropertySource注解。

1、注解源碼

Spring中提供了@PropertySource和@PropertySources兩個注解來加載配置文件。

(1)@PropertySource注解

@PropertySource注解只能標(biāo)注到類上,能夠通過指定配置文件的位置來加載配置文件,@PropertySource注解除了可以加載properties配置文件外,也可以加載xml配置文件和yml配置文件。如果加載yml配置文件時,可以自定義PropertySourceFactory實現(xiàn)yml配置文件的解析操作。

@PropertySource注解的源碼詳見:org.springframework.context.annotation.PropertySource。

/**
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.1
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
String name() default "";
String[] value();
/**
* @since 4.0
*/
boolean ignoreResourceNotFound() default false;
/**
* @since 4.3
*/
String encoding() default "";
/**
* @since 4.3
*/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}

從源碼可以看出,@PropertySource注解是從Spring3.1版本開始提供的注解,注解中各個屬性的含義如下所示。

  • name:表示加載的資源的名稱,如果為空,則會根據(jù)加載的配置文件自動生成一個名稱。
  • value:表示加載的資源的路徑,這個路徑可以是類路徑,也可以是文件路徑。
  • ignoreResourceNotFound:表示當(dāng)配置文件未找到時,是否忽略文件未找到的錯誤。默認(rèn)值為false,也就是說當(dāng)未找到配置文件時,Spring啟動就會報錯。
  • encoding:表示解析配置文件使用的字符集編碼。
  • factory:表示讀取對應(yīng)配置文件的工廠類,默認(rèn)的工廠類是PropertySourceFactory。

(2)@PropertySources注解

除了@PropertySource注解,Spring中還提供了一個@PropertySources注解。@PropertySources注解中的源碼詳見:org.springframework.context.annotation.PropertySources。

/**
* @author Phillip Webb
* @since 4.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PropertySources {
PropertySource[] value();
}

從源碼可以看出,@PropertySources是從Spring4.0版本開始提供的注解,在@PropertySources注解中,只提供了一個PropertySource數(shù)組類型的value屬性。所以,@PropertySources注解可以引入多個@PropertySource注解。

2、注解使用場景

在基于Spring的注解開發(fā)項目的過程中,由于不再使用Spring的XML文件進(jìn)行配置,如果將配置項直接寫到類中,就會造成配置項與類的緊耦合,后續(xù)對于配置項的修改操作非常不方便,不利于項目的維護(hù)和擴(kuò)展。此時,可以將這些配置項寫到properties文件或者yml文件中,通過@PropertySource注解加載配置文件。

另外,如果項目本身就存在大量的properties配置文件或者yml配置文件,也可以統(tǒng)一由Spring的@PropertySource注解進(jìn)行加載。

三、使用案例

結(jié)合案例學(xué)著印象才會更深刻~~

本節(jié),主要實現(xiàn)一個通過@PropertySource注解加載properties配置文件,將properties配置文件中的配置項加載到Spring的環(huán)境變量中,獲取Spring環(huán)境變量中配置項的值,并進(jìn)行打印。案例的具體實現(xiàn)步驟如下所示。

1、新增test.properties文件

在spring-annotation-chapter-06工程的resources目錄下新增test.properties文件,文件內(nèi)容如下所示。

name=binghe
age=18

2、新增PropertySourceConfig類

PropertySourceConfig類的源碼詳見:spring-annotation-chapter-06工程下的io.binghe.spring.annotation.chapter06.config.PropertySourceConfig。

@Configuration
@PropertySource(value = "classpath:test.properties")
public class PropertySourceConfig {
}

可以看到,PropertySourceConfig類是Spring的配置類,并且使用@PropertySource注解指定了test.properties配置文件的路徑。

3、新增PropertySourceTest類

PropertySourceTest類的源碼詳見:spring-annotation-chapter-06工程下的io.binghe.spring.annotation.chapter06.PropertySourceTest。

public class PropertySourceTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PropertySourceConfig.class);
ConfigurableEnvironment environment = context.getEnvironment();
System.out.println(environment.getProperty("name") + " ====>>> " + environment.getProperty("age"));
}
}

可以看到,在PropertySourceTest類的main()方法中,通過AnnotationConfigApplicationContext類的對象獲取到ConfigurableEnvironment類型的環(huán)境變量對象environment,然后通過environment對象獲取配置文件中的name和age的值并進(jìn)行打印。

4、運行PropertySourceTest類

運行PropertySourceTest類的main()方法,輸出的結(jié)果信息如下所示。

binghe ====>>> 18

可以看到,正確的輸出了配置文件中的值。

說明:使用@PropertySource注解可以加載properties配置文件中的配置項,并將配置項加載到Spring的環(huán)境變量中,通過Spring的環(huán)境變量就可以獲取到配置項的值。

四、源碼時序圖

結(jié)合時序圖理解源碼會事半功倍,你覺得呢?

本節(jié),就以源碼時序圖的方式,直觀的感受下@PropertySource注解在Spring源碼層面的執(zhí)行流程。@PropertySource注解在Spring源碼層面的執(zhí)行流程如圖6-1~6-2所示。

圖片

圖6-1

圖片

圖6-2

由圖6-1~圖6-2可以看出,@PropertySource注解在Spring源碼層面的執(zhí)行流程會涉及到PropertySourceTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、PostProcessorRegistrationDelegate類、ConfigurationClassPostProcessor類、ConfigurationClassParser類、PropertySourceRegistry類、PropertySourceProcessor類和DefaultPropertySourceFactory類。具體的源碼執(zhí)行細(xì)節(jié)參見源碼解析部分。

五、源碼解析

源碼時序圖整清楚了,那就整源碼解析唄!

@PropertySource注解在Spring源碼層面的執(zhí)行流程,結(jié)合源碼執(zhí)行的時序圖,會理解的更加深刻。

(1)運行案例程序啟動類。

案例程序啟動類源碼詳見:spring-annotation-chapter-06工程下的io.binghe.spring.annotation.chapter06.PropertySourceTest,運行PropertySourceTest類的main()方法。

在PropertySourceTest類的main()方法中調(diào)用了AnnotationConfigApplicationContext類的構(gòu)造方法,并傳入了PropertySourceConfig類的Class對象來創(chuàng)建IOC容器。接下來,會進(jìn)入AnnotationConfigApplicationContext類的構(gòu)造方法。

注意:@PropertySource注解在Spring源碼中的執(zhí)行流程的(2)~(11)步與第5章的@Import注解相同,這里不再贅述,直接跳到ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法。

(2)解析ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法。

源碼詳見:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter),重點關(guān)注如下代碼片段。

protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
//#############省略其他代碼################
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.propertySourceRegistry != null) {
this.propertySourceRegistry.processPropertySource(propertySource);
}
}
//#############省略其他代碼################
}

可以看到,在ConfigurationClassParser類的doProcessConfigurationClass()方法中,遍歷獲取到的@PropertySources注解和@PropertySource注解的屬性,并且調(diào)用propertySourceRegistry對象的processPropertySource()方法解析注解屬性的值。

(3)解析PropertySourceRegistry類的processPropertySource(AnnotationAttributes propertySource)方法。

源碼詳見:org.springframework.context.annotation.PropertySourceRegistry#processPropertySource(AnnotationAttributes propertySource)。

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");
Class<? extends PropertySourceFactory> factorClassToUse = (factoryClass != PropertySourceFactory.class ? factoryClass : null);
PropertySourceDescriptor descriptor = new PropertySourceDescriptor(Arrays.asList(locations), ignoreResourceNotFound, name, factorClassToUse, encoding);
this.propertySourceProcessor.processPropertySource(descriptor);
this.descriptors.add(descriptor);
}

可以看到,在PropertySourceRegistry類的processPropertySource()方法中,解析@PropertySource注解中的屬性后,將解析出的屬性值封裝到PropertySourceDescriptor對象中,調(diào)用propertySourceProcessor對象的processPropertySource()方法,并傳入PropertySourceDescriptor對象進(jìn)行進(jìn)一步處理。

(4)解析PropertySourceProcessor類的processPropertySource(PropertySourceDescriptor descriptor)方法。

源碼詳見:org.springframework.core.io.support.PropertySourceProcessor#processPropertySource(PropertySourceDescriptor descriptor)。

public void processPropertySource(PropertySourceDescriptor descriptor) throws IOException {
String name = descriptor.name();
String encoding = descriptor.encoding();
List<String> locations = descriptor.locations();
Assert.isTrue(locations.size() > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = descriptor.ignoreResourceNotFound();
PropertySourceFactory factory = (descriptor.propertySourceFactory() != null ? instantiateClass(descriptor.propertySourceFactory()) : DEFAULT_PROPERTY_SOURCE_FACTORY);
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) {
//#########省略其他代碼################
}
}
}

可以看到,在processPropertySource()方法中,會通過@PropertySource注解的屬性值解析出配置文件的內(nèi)容,并且通過factory對象的createPropertySource()方法來創(chuàng)建PropertySource對象。

(5)解析DefaultPropertySourceFactory類的createPropertySource(String name, EncodedResource resource)方法。

源碼詳見:org.springframework.core.io.support.DefaultPropertySourceFactory#createPropertySource(String name, EncodedResource resource)。

@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}

createPropertySource()方法的源碼比較簡單,不再贅述。

(6)回到PropertySourceProcessor類的processPropertySource(PropertySourceDescriptor descriptor)方法。

在PropertySourceProcessor類的processPropertySource()方法中,創(chuàng)建完P(guān)ropertySource對象后,會調(diào)用addPropertySource()方法將獲取到的屬性值添加到Spring的環(huán)境變量中。

(7)解析PropertySourceProcessor類的addPropertySource(PropertySource<?> propertySource)方法。

源碼詳見:org.springframework.core.io.support.PropertySourceProcessor#addPropertySource(PropertySource<?> propertySource)。

private void addPropertySource(org.springframework.core.env.PropertySource<?> propertySource) {
String name = propertySource.getName();
MutablePropertySources propertySources = this.environment.getPropertySources();
if (this.propertySourceNames.contains(name)) {
org.springframework.core.env.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);
}

可以看到,在PropertySourceProcessor類的addPropertySource()方法中,會將解析出的配置文件的內(nèi)容添加到Spring的環(huán)境變量中。具體就是在PropertySourceProcessor類的addPropertySource()方法中,獲取到ConfigurableEnvironment中的MutablePropertySources對象,用來存儲解析出的配置文件中的配置項內(nèi)容。如果有相同的配置項內(nèi)容,將existing對象強轉(zhuǎn)為CompositePropertySource類型,把新舊相同的配置項進(jìn)行合并,再放到MutablePropertySources對象中。

后續(xù)就可以通過Spring的環(huán)境變量,來獲取到配置文件中的配置項內(nèi)容。

至此,@PropertySource注解在Spring源碼中的執(zhí)行流程分析完畢。

六、總結(jié)

@PropertySource注解講完了,我們一起總結(jié)下吧!

本章,首先介紹了@PropertySource注解的源碼和使用場景,隨后,簡單給出了一個@PropertySource注解的使用案例。接下來,詳細(xì)分析了@PropertySource注解的源碼時序圖和@PropertySource注解在Spring源碼層面的執(zhí)行流程。

七、思考

既然學(xué)完了,就開始思考幾個問題吧?

關(guān)于@PropertySource注解,通常會有如下幾個經(jīng)典面試題:

  • @PropertySource注解的執(zhí)行流程?
  • @PropertySource注解是如何將配置文件加載到環(huán)境變量的?
  • @PropertySource注解有哪些使用場景?
  • Spring中為何會設(shè)計一個@PropertySource注解來加載配置文件?
  • 從@PropertySource注解的設(shè)計中,你得到了哪些啟發(fā)?
責(zé)任編輯:姜華 來源: 冰河技術(shù)
相關(guān)推薦

2023-03-27 08:12:40

源碼場景案例

2023-03-13 08:12:25

@DependsOn源碼場景

2023-02-27 08:10:00

代理對象Spring

2023-05-08 08:11:49

@Component使用場景時序圖

2022-12-22 08:14:54

2023-05-29 08:11:42

@Value注解Bean

2024-01-11 12:14:31

Async線程池任務(wù)

2013-12-09 10:34:12

2023-10-10 11:02:00

LSM Tree數(shù)據(jù)庫

2024-12-17 00:00:00

Spring線程

2019-03-06 09:55:54

Python 開發(fā)編程語言

2011-06-02 11:13:10

Android Activity

2011-08-02 18:07:03

iPhone 內(nèi)省 Cocoa

2012-08-03 08:57:37

C++

2023-10-12 13:01:29

Redis數(shù)據(jù)庫

2013-07-02 10:08:46

爛代碼代碼優(yōu)化代碼清理

2021-10-12 11:07:33

動畫深度Android

2009-12-14 17:14:08

Ruby文件操作

2011-06-27 09:15:21

QT Creator

2011-07-29 15:09:48

iPhone Category
點贊
收藏

51CTO技術(shù)棧公眾號