在Spring框架中@PropertySource注解是非常常用的一个注解,其主要作用是将外部化配置解析成key-value键值对"存入"Spring容器的Environment环境中,以便在Spring应用中可以通过@Value或者占位符${key}的形式来使用这些配置。

使用案列

// @PropertySource需要和@Configuration配个使用
// @PropertySource加载的配置文件时需要注意加载的顺序,后面加载的配置会覆盖前面加载的配置
// @PropertySource支持重复注解
// value值不仅支持classpath表达式,还支持任意合法的URI表达式
@Configuration
@PropertySource(value = "classpath:/my.properties",encoding = "UTF8")
@PropertySource(value = "classpath:/my2.properties",encoding = "UTF8",ignoreResourceNotFound = true)
public static class PropertyConfig {
} @Component
public class App {
@Value("${key1:default-val}")
private String value; @Value("${key2:default-val2}")
private String value2;
}

下面是配置文件my.properties和my2.properties的具体内容。

# my.properties
key1=自由之路 # my2.properties
key1=程序员
key2=自由之路

Spring容器启动时,会将my.properties和my2.properties的内容加载到Environment中,并在App类的依赖注入环节,将key1和key2的值注入到对应的属性。

自定义PropertySource工厂

阅读@PropertySource的源代码,我们发现还有一个factory属性。从这个属性的字面意思看,我们不难猜测出这个属性设置的是用于产生PropertySource的工厂。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource { String name() default ""; String[] value(); boolean ignoreResourceNotFound() default false; String encoding() default ""; Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class; }

要深入理解PropertySourceFactory,我们先要知道以下的背景知识。

在Spring中,配置的来源有很多。Spring将配置来源统一抽象成 PropertySource 这个抽象类,Spring中内建的常用的 PropertySource 有以下这些

  • MapPropertySource

  • CommandLinePropertySource

  • PropertiesPropertySource

  • SystemEnvironmentPropertySource

  • ResourcePropertySource

ResourcePropertySource这个类将一系列配置来源统一成ResourcePropertySource,可以说是对 PropertySource 的进一步封装。

PropertySourceFactory 接口,用于产生PropertySource。Spring中,PropertySourceFactory 默认的实现是DefaultPropertySourceFactory,用于生产 ResourcePropertySource。

经过上面的介绍,我们知道如果没有配置@PropertySource的factory属性的话,默认的PropertySourceFactory使用的就是DefaultPropertySourceFactory。当然,我们也可以自定义PropertySourceFactory,用于“生产”我们自定义的PropertySource。下面就演示一个将yaml文件解析成MapPropertySource的使用案列。

/**
* Spring中内置的解析yaml的处理器
* YamlProcessor
* - YamlMapFactoryBean --> 解析成Map
* - YamlPropertiesFactoryBean --> 解析成Properties
*/
public class YamlMapSourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
YamlMapFactoryBean yamlMapFactoryBean = new YamlMapFactoryBean();
yamlMapFactoryBean.setResources(resource.getResource());
Map<String, Object> map = yamlMapFactoryBean.getObject();
return new MapPropertySource(name, map);
}
} // 加了factory属性,必须加name属性
// 有了factory机制,我们可以做很多自定一的扩展,比如配置可以从远程来
@PropertySource(name = "my.yaml",value = "classpath:/my.yaml",encoding = "UTF8",factory = YamlMapSourceFactory.class)
public static class PropertyConfig {
}

原理简析

到这边我们对@PropertySource已经有了一个感性的认识,知道了其主要作用是将各种类型的外部化配置文件以key-value的形式加载到Spring的Environment中。这个部分我们从源码的角度来分析下Spring是怎么处理@PropertySource这个注解的。分析源码可以加深我们对@PropertySource的认识(看源码不是目的,是为了加深理解,学习Spring的设计思想)。

@PropertySource注解的处理是在ConfigurationClassPostProcessor中进行触发的。最终会调用到ConfigurationClassParser的processPropertySource方法。

// ConfigurationClassParser#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");
// 如果有自定义工厂就使用自定义工厂,没有自定义工厂就使用DefaultPropertySourceFactory
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
// 遍历各个location地址
for (String location : locations) {
try {
// location地址支持占位符的形式
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
// 获取Resource
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;
}
}
}
}

总的来说,Spring处理@PropertySource的源代码非常简单,这边就不再过多赘述了。

Spring注解系列——@PropertySource的更多相关文章

  1. Spring注解 系列之Spring常用注解总结

    参考:Spring系列之Spring常用注解总结 (1) Resource 默认是byName的方式进行bean配置,@AutoWired默认是按照byType的方式进行装配bean的:(2)Comp ...

  2. Spring 注解(二)注解工具类 AnnotationUtils 和 AnnotatedElementUtils

    Spring 注解(二)注解工具类 AnnotationUtils 和 AnnotatedElementUtils Spring 系列目录(https://www.cnblogs.com/binary ...

  3. Spring 注解(一)Spring 注解编程模型

    Spring 注解(一)Spring 注解编程模型 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring 注解系列 ...

  4. Spring注解开发系列专栏

    这个系列主要是讲Spring注解的使用,可以为后面SpringBoot的学习带来一定的帮助.我觉得从Spring直接过度到SpringBoot还是有点快,还是得需要一个演变的过程.从Spring开发, ...

  5. Spring注解开发系列Ⅳ --- 属性赋值

    在Spring框架中,属性的注入我们有多种方式,我们可以通过构造方法注入,可以通过set方法注入,也可以通过p名称空间注入,方式多种多样,对于复杂的数据类型比如对象.数组.List集合.map集合.P ...

  6. 【Spring注解驱动开发】使用@PropertySource加载配置文件,我只看这一篇!!

    写在前面 很多小伙伴都在问:冰河,你的Spring专题更新完了吗?怎么感觉像是写了一半啊?我:没有更新完呀,整个专题预计会有70多篇.那怎么更新了一半就去写别的了呢?那是因为有很多其他的小伙伴在后台留 ...

  7. Spring注解开发系列Ⅵ --- AOP&事务

    注解开发 --- AOP AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,横向重复,纵向抽取.详细的AO ...

  8. Spring Cloud系列之Commons - 1. 背景与基础知识准备

    本文基于 Spring Cloud 2020.0 发布版的依赖 本系列会深入分析 Spring Cloud 的每一个组件,从Spring Cloud Commons这个 Spring Cloud 所有 ...

  9. Spring AOP 系列总括

    Spring有两大核心,IOC和AOP.IOC在Java Web项目中无时无刻不在使用,然而AOP用的比较少,尤其是对一些初级程序员,在架构师搭好的框架上开发应用代码,AOP几乎是透明的.然而,项目中 ...

  10. 转-Spring 注解学习手札(七) 补遗——@ResponseBody,@RequestBody,@PathVariable

    转-http://snowolf.iteye.com/blog/1628861/ Spring 注解学习手札(七) 补遗——@ResponseBody,@RequestBody,@PathVariab ...

随机推荐

  1. 【xUtils框架问题】xUtils继承基类的x.view().inject(this)绑定点击事件@Event无效

    由于看得教程里的xUtils比较老了,不知道什么版本的. 还是使用ViewUtils.inject()进行绑定反射的,使用@OnClick进行点击监听绑定的. 3.9.0版本的xUtils使用: x. ...

  2. CSS设置边距

    1.内边距 所有的 ​HTML ​元素基本都是以矩形为基础. 每个 HTML 元素周围的矩形空间由三个重要的属性来控制: ​padding(内边距)​ ​margin(外边距)​ ​border(边框 ...

  3. redis 常用指令

    redis指令有些相似,记忆起来不太容易,在此做一下整理 序号 类型 指令 参数 作用 例子 1 string set key value 存储一个 string 类型的值 set a aa 2 st ...

  4. 弹框tabel树

    <template> <div> <el-dialog :title="title" :visible.sync="dialogVisibl ...

  5. java不返回某些字段,包括 null

    一.使用 fastjson 包. (1) SimplePropertyPreFilter 可以将需要的字段留下来. import com.alibaba.fastjson.JSON;import co ...

  6. oracle中的merge into用法解析

    一:merge into的形式 MERGE INTO [target-table] A USING [source-table sql] B ON([conditional expression] a ...

  7. filebeat+elasticsearch+kibana

    一.到elasticsearch官网下载 filebeat+elasticsearch+kibana http://www.elasticsearch.cn/ 二.安装filebeat tar -xz ...

  8. Postman挂载外部文件,实现参数化

    一般来说,对一个接口进行测试,只能用边界值和等价类的方法,因此就会涉及到各种参数,使用Postman参数化,比较便捷: 但是自己这篇写得实在太浅陋了,将流花兄的博客内容附下,可以直接看他的 https ...

  9. 2022.07.25 TypeScript基础类型介绍

    基础类型: 字符串(string)(String) let first: string = 'niu' // 直接赋值 let fourth: string = `niu` // 模板字符串 let ...

  10. C# DataTable操作,转载

    DataTable 排序   DataRow[] rows = dataTable1.Select("", "ord asc");   DataTable t ...