您当前的位置:滚动 > >正文
Spring注解系列——@PropertySource

时间:2023-04-07 11:27:40   来源:博客园

在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 {}@Componentpublic class App {    @Value("${key1:default-val}")    private String value;    @Value("${key2:default-val2}")    private String value2;}

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


【资料图】

# my.propertieskey1=自由之路# my2.propertieskey1=程序员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 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 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#processPropertySourceprivate 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 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的源代码非常简单,这边就不再过多赘述了。

标签:

精心推荐