码迷,mamicode.com
首页 > 数据库 > 详细

在Spring-boot中,为@Value注解添加从数据库读取properties支持

时间:2019-09-09 14:48:48      阅读:326      评论:0      收藏:0      [点我收藏+]

标签:select   view   error   log   views   catch   注入   map   ret   

一般我们会把常用的属性放在工程的classpath文件夹中,以property,yaml或json的格式进行文件存储,便于Spring-boot在初始化时获取。 

@Value则是Spring一个非常有用的注解,可以在初始化时很方便的对Bean的入参变量进行赋值,例如:

@Bean
    public BusinessClient businessClient (@Value("http://baseUrl/") String baseUrl) {
        Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        return retrofit.create(BusinessClient .class);

于是,初始化好的Business Client进行http请求时,默认的baseurl都是”http://baseUrl“。
实际上,@Value还支持一种特殊的写法:”${some.proptery.key}”,即将property的key值写在花括号中。例如,我有一个property为aerexu.basurl=http://baseUrl2,将上例中的@Value改写成@Value("${aerexu.basurl}"),baseUrl实际获取到的值是http://baseUrl2。 
这个特性是利用Spring的bean PropertySourcesPlaceholder实现的,Spring boot已经在初始化时帮我们自动实例化了该bean。若是传统的Spring工程,则需要主动实例化,如下:

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
        configurer.setPlaceholderPrefix(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_PREFIX);
        configurer.setPlaceholderSuffix(PlaceholderConfigurerSupport.DEFAULT_PLACEHOLDER_SUFFIX);
        configurer.setValueSeparator(PlaceholderConfigurerSupport.DEFAULT_VALUE_SEPARATOR);  
        return configurer;
    }

 

一般情况下,property存在工程中的文件就可以了,但带来的坏处是如果属性需要改变,必须重新发布工程。比如,对接上例中的url,可能会变为https,可能端口会变化。所以,这种类型的属性放在数据库中更合适。
然而将属性存储在数据库中后,@Value对应的值就无法正常解析了。因此,这里提供一种hack的方法,使得@Value可以正常解析。
PropertySourcesPlaceholder在解析属性时,都是从ConfigurableEnvironment中进行寻找的。当ConfigurableEnvironment没有存在的属性时,${}写法的@Value就无法解析了。因此,需要通过特殊的处理,将存储在数据库中的属性注入到ConfigurableEnvironment中。本文定义了一个LoadFromDatabasePropertyConfig类实现该功能,其代码如下:

    @Configuration
    @Slf4j
    public class LoadFromDatabasePropertyConfig {
        @Autowired
        private ConfigurableEnvironment env;
        @Autowired
        private SysPropertyResourceMapper propertyResourceMapper;
        @PostConstruct
        public void initializeDatabasePropertySourceUsage() {
            MutablePropertySources propertySources = env.getPropertySources();
            try {
                Map<String, Object> propertyMap = propertyResourceMapper.selectAll().stream()
                        .collect(Collectors.toMap(SysPropertyResource::getPropertyName, SysPropertyResource::getPropertyValue));
                Properties properties = new Properties();
                properties.putAll(propertyMap);
                PropertiesPropertySource dbPropertySource = new PropertiesPropertySource("dbPropertySource", properties);
                Pattern p = Pattern.compile("^applicationConfig.*");
                String name = null;
                boolean flag = false;
                for (PropertySource<?> source : propertySources) {
                    if (p.matcher(source.getName()).matches()) {
                        name = source.getName();
                        flag = true;
                        log.info("Find propertySources ".concat(name));
                        break;
                    }
                }
                log.info("=========================================================================");
                if(flag) {
                    propertySources.addBefore(name, dbPropertySource);
                } else {
                    propertySources.addFirst(dbPropertySource);
                }
            } catch (Exception e) {
                log.error("Error during database properties setup", e);
                throw new RuntimeException(e);
            }
        }
    }

上述代码的具体思路是将数据库中的所有需要的属性读出,通过Properties类转换为Spring可用的PropertiesPropertySource,并取名为dbPropertySource。随后利用正则匹配,从已有的所有属性中找到名称以applicationConfig开头的属性(该属性即是所有配置在文件中的property所解析成的对象),并将dbPropertySource存储在其之前。这样当文件和数据库中同时存在key相等的属性时,会优先使用数据库中存储的value。
需要注意的是,上述方案提供的属性解析,必须在数据库相关的bean都实例化完成后才可进行。且为了保证bean在实例化时,数据库属性已经被加入到ConfigurableEnvironment中去了,必须添加@DependsOn注解。上面的BusinessClient的实例化就需更新成:

    @Bean
    @DependsOn("loadFromDatabasePropertyConfig")
    public BusinessClient businessClient (@Value("${aerexu.basurl}") String baseUrl) {
        Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        return retrofit.create(BusinessClient .class);

在Spring-boot中,为@Value注解添加从数据库读取properties支持

标签:select   view   error   log   views   catch   注入   map   ret   

原文地址:https://www.cnblogs.com/bevis-byf/p/11491475.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!