Spring中的@PropertySource注解源碼詳解
@PropertySource注解使用
@PropertySource注解用于指定資源文件讀取的位置,它不僅能讀取properties文件,也能讀取xml文件,并且通過yaml解析器,配合自定義PropertySourceFactory實現解析yaml文件
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Repeatable(PropertySources.class) public @interface PropertySource { //資源名稱,為空則根據資源的描述符生成 String name() default ""; /** *資源路徑 *classpath:application.properties *file:/ */ String[] value(); //是否忽略資源不存在的情況,如果不忽略,當資源不存在時報錯 boolean ignoreResourceNotFound() default false; //指定資源文件的編碼格式 String encoding() default ""; //資源工廠 Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class; }
源碼解析
在bean實例化之前有一個ConfigurationClassPostProcessor類,會操作BeanDefinition并且加入到BeanDefinitionRegistry中,它的優(yōu)先級在所有的BeanDefinitionRegistryPostProcessor里面是最低的。
優(yōu)先級:最低
public int getOrder() { return Ordered.LOWEST_PRECEDENCE; // within PriorityOrdered }
會在這個類中進行相關注解的解析操作,在進行@PropertySource注解解析的時候要借助ConfigurationClassParser的parse方法
在進行@PropertySource注解解析之前,需要拿到兩個對象ConfigurationClass和SourceClass
bean的元數據信息(bd.getMetadata),和beanName,封裝成ConfigurationClass對象
new ConfigurationClass(metadata, beanName)
SourceClass這個對象理解為跟類或者接口對應,然后把metadata對象包裝進去
SourceClass sourceClass = asSourceClass(configClass, filter);
接下來正式進入解析流程
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( 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"); } }
拿到類上面的所有PropertySource注解的值AnnotationAttributes,放到processPropertySource方法中進行解析
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 resource = this.resourceLoader.getResource(resolvedLocation); //加載Resource中的配置屬性封裝成Properties對象中,并創(chuàng)建PropertySource對象加入到Environment對象中 addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding))); } catch (IllegalArgumentException | FileNotFoundException | UnknownHostException 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; } } } }
這里看一下createPropertySource方法,就是將配置文件中的內容封裝成ResourcePropertySource對象,作為屬性源
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException { return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource)); }
屬性源拿到之后,要統(tǒng)一交給Environment管理,調用addPropertySource方法
private void addPropertySource(PropertySource<?> propertySource) { String name = propertySource.getName(); //獲取Environment對象中的MutablePropertySources MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources(); //如果已經存在了該配置文件的PropertySource則合并久的 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里面有一個Set,Set里面裝了新和舊的PropertySource對象 CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(newSource); composite.addPropertySource(existing); propertySources.replace(name, composite); } return; } } if (this.propertySourceNames.isEmpty()) { propertySources.addLast(propertySource); } else { //用于計算插入的位置index String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1); //吧propertySource對象存入MutablePropertySources的list中 propertySources.addBefore(firstProcessed, propertySource); } this.propertySourceNames.add(name); }
拿到Environment中的MutablePropertySources,用來放置拿到的屬性源。 如果有相同的屬性源,則升級成CompositePropertySource,把新舊相同的屬性源進行合并,再放到MutablePropertySources中
看看CompositePropertySource類的內部 有Set<PropertySource<?>> propertySources = new LinkedHashSet<>();用來放置屬性源 也重寫了getProperty方法
public Object getProperty(String name) { for (PropertySource<?> propertySource : this.propertySources) { Object candidate = propertySource.getProperty(name); if (candidate != null) { return candidate; } } return null; }
@PropertySource注解中配置的屬性源都交給Environment管理
到此這篇關于Spring中的@PropertySource注解源碼詳解的文章就介紹到這了,更多相關@PropertySource注解源碼內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實現DES加密與解密,md5加密以及Java實現MD5加密解密類
這篇文章主要介紹了Java實現DES加密與解密,md5加密以及Java實現MD5加密解密類 ,需要的朋友可以參考下2015-11-11idea配置springboot熱部署終極解決辦法(解決熱部署失效問題)
這篇文章主要介紹了idea配置springboot熱部署終極解決辦法(解決熱部署失效問題),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2020-07-07Springboot集成MongoDB無認證與開啟認證的配置方式
本文主要介紹了Springboot集成MongoDB無認證與開啟認證的配置方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-03-03java集合——Java中的equals和hashCode方法詳解
本篇文章詳細介紹了Java中的equals和hashCode方法詳解,Object 類是所有類的父類,非常具有實用價值,需要的朋友可以參考下。2016-10-10SpringBoot整合Netty實現WebSocket的示例代碼
本文主要介紹了SpringBoot整合Netty實現WebSocket的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-05-05