美文网首页
Spring动态替换Properties配置变量

Spring动态替换Properties配置变量

作者: Sanisy | 来源:发表于2019-01-24 15:29 被阅读8次

    Spring动态替换Properties配置变量

    有如下需求,在Spring启动的时候,我们需要修改某个指定的配置的值,从而达到动态加载变量的效果:

        @Value("${destination}")
        public String destination;
    

    1.实现方法有如下:

    • 在对应的Spring Bean实例化之后,通过反射动态的修改实例的参数值
    • 动态的修改PropertySource

    具体的类如下:

    public interface HelloService {
    
        /**
         * say hello
         */
        void sayHello();
    }
    
    
    @Service
    public class HelloServiceImpl implements HelloService{
    
        @Value("${destination}")
        private String destination;
    
        @Override
        public void sayHello() {
            System.out.println("Hello, let us go to " + destination);
        }
    }
    

    第一种方法实现

    方式1:
    定义一个应用事件,监听应用上下文刷新事件,然后我们可以通过这个事件拿到ApplicationContext上下文,进而去获取对应的SpringBean,然后进行反射赋值操作。

    public class LocalApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            Object helloService = event.getApplicationContext().getBean(HelloService.class);
            if (helloService instanceof HelloServiceImpl) {
                try {
                    Field field = HelloServiceImpl.class.getDeclaredField("destination");
                    field.setAccessible(true);
                    ReflectionUtils.setField(field, helloService, "广东深圳");
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    有了这个监听事件,我们需要把这个监听器添加到Spring的监听器集合中,可以通过启动类的启动方法进行添加:

    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication app = new SpringApplication(DemoApplication.class);
            app.addListeners(new LocalApplicationListener());
            app.run(args);
        }
    }
    

    方式2:
    定义一个类实现BeanPostProcessor接口,在接口中进行反射操作。BeanPostProcessor是Spring提供的一个Bean扩展接口,可以通过该接口实现一些Bean创建之前和创建之后的操作。

    @Component
    public class LocalBeanProcessor implements BeanPostProcessor {
    
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (bean instanceof HelloServiceImpl) {
                try {
                    Field field = HelloServiceImpl.class.getDeclaredField("destination");
                    field.setAccessible(true);
                    ReflectionUtils.setField(field, bean, "广东广州");
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                }
            }
            return bean;
        }
    }
    

    第二种实现方法
    通过了解Spring的加载流程,我们得知Spring在上下文准备完毕(配置信息解析完毕并创建好了应用上下文)之后,会调用推送一个ApplicationEnvironmentPreparedEvent,从这个事件中,我们可以获取到ConfigurableEnvironment对象,而ConfigurableEnvironment对象可以获取到MutablePropertySources。对于MutablePropertySources,它包含了Spring的所有 的配置信息,包括我们启动应用的application.properties文件的配置信息。具体代码如下:

    public class LocalEnvironmentPrepareEventListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
    
        @Override
        public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
            MutablePropertySources propertySources = event.getEnvironment().getPropertySources();
            for (PropertySource<?> propertySource : propertySources) {
                boolean applicationConfig = propertySource.getName().contains("applicationConfig");
                if (!applicationConfig) {
                   continue;
                }
                Object property = propertySource.getProperty("destination");
                Map<String, OriginTrackedValue> source = (Map<String, OriginTrackedValue>) propertySource.getSource();
                OriginTrackedValue originTrackedValue = source.get("destination");
                OriginTrackedValue newOriginTrackedValue = OriginTrackedValue.of("中国香港", originTrackedValue.getOrigin());
                source.put("destination", newOriginTrackedValue);
                System.out.println(property);
            }
        }
    }
    

    同样的我们需要把该监听器添加到Spring中:

    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication app = new SpringApplication(DemoApplication.class);
            app.addListeners(new LocalEnvironmentPrepareEventListener());
            app.run(args);
        }
    }
    

    知名的分布式配置管理工具Apollo就是通过反射来实现配置参数的动态修改的,Apollo实现了BeanPostProcessor接口,这样它就可以把所有的Bean和@Value的key的关系保存起来,类似于Map<String, List<Bean>>,当配置中心的配置被改动的时候,就发一个通知给对应的服务,然后由服务自己去拉取配置参数,重新赋值。

    相关文章

      网友评论

          本文标题:Spring动态替换Properties配置变量

          本文链接:https://www.haomeiwen.com/subject/qhzyjqtx.html