• Spring注解系列——@PropertySource


    在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 "";
    
    	Classextends 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 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");
    
        Classextends 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的源代码非常简单,这边就不再过多赘述了。

  • 相关阅读:
    Flutter学习9 - http 中 get/post 请求示例
    如何在 PyGame 中初始化所有导入的模块
    Linux系统之时间同步方法
    无代码 10 月资讯 |Zapier 推出创新计划;微软产品大更新;Gartner发布《2023年十大战略技术趋势》
    全国计算机四级之网络工程师知识点(三)
    sealos 离线安装k8s
    论坛介绍|COSCon'23 开源百宝箱(T)
    maven基础学习
    wqs二分+斜率优化:1019T4 / P9338
    CSS实现空心的“尖角”
  • 原文地址:https://www.cnblogs.com/54chensongxia/p/17295503.html