您现在的位置是:首页 >技术交流 >spring 热更新配置网站首页技术交流

spring 热更新配置

伊布拉西莫 2025-06-23 12:01:03
简介spring 热更新配置

我们经常会遇到不重启服务,自动刷新配置的需求。该如何实现呢?

@Scope(“prototype”) - 热更新

spring prototype

如上文中的BarService,每次变更配置文件中的x.bar配置信息,都能及时应用到service中。

@Slf4j
@Component
@Scope("prototype") // prototype原型模式
public class BarService {

    @Value("${x.bar}")
    private String bar;

    public void bar() {
        log.info("---bar:{} ,{}", bar, this);
    }
}

code

ConfigRefreshEvent
public class ConfigRefreshEvent extends ApplicationEvent {
    @Getter
    @Setter
    private String path;

    public ConfigRefreshEvent(Object source) {
        super(source);
    }

    public ConfigRefreshEvent(Object source, String path) {
        super(source);
        this.path = path;
    }
}
ConfigRefreshEventListener
@Component
public class ConfigRefreshEventListener implements ApplicationListener<ConfigRefreshEvent> {
    @Autowired
    ConfigurableEnvironment configurableEnvironment;

    @Autowired
    private GenericApplicationContext applicationContext;

    @Override
    public void onApplicationEvent(ConfigRefreshEvent event) {
        log.info("----------ConfigRefreshEventListener ConfigRefreshEvent Start----------");
        File file = new File(event.getPath());
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            Properties properties = new Properties();
            properties.load(fileInputStream);
            PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource("URL [file:" + CONFIG_PATH + "]", properties);
            //更新配置信息
            configurableEnvironment.getPropertySources().addFirst(propertiesPropertySource);
            applicationContext.setEnvironment(configurableEnvironment);

            log.info("Properties dynamic update completed");
        } catch (Exception e) {
            log.error("ConfigRefreshEventListener Exception ", e);
        }
        log.info("----------ConfigRefreshEventListener ConfigRefreshEvent End----------");
    }
}
ConfigRefreshEventFireScheduler

定义一个Scheduler,监控配置文件内容是否变化。

@Component
@Primary
@Slf4j
public class ConfigRefreshEventFireScheduler {
    private long lastUpdateTime = 0;

    @Autowired
    ApplicationContext applicationContext;


    @Scheduled(fixedRate = 10, timeUnit = TimeUnit.SECONDS)
    public void checkAndNotify() {
        String batchNo = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmSS"));
        log.debug("---Scheduler:{}  start...", batchNo);
        File file = new File(Constants.CONFIG_PATH);
        if (file.isFile()) {
            long fileLastModified = file.lastModified();
            if (fileLastModified > this.lastUpdateTime) {
                if (this.lastUpdateTime == 0) {
                    this.lastUpdateTime = fileLastModified;
                } else {
                    this.lastUpdateTime = fileLastModified;
                    ConfigRefreshEvent event = new ConfigRefreshEvent(new Object(), Constants.CONFIG_PATH);
                    this.applicationContext.publishEvent(event);
                    log.debug("---Scheduler:{}  finish...", batchNo);
                    return;
                }
            }

            log.debug("---Scheduler:{}  skip...", batchNo);

        }
    }
}

修改配置文件

x.bar=BAR5
x.foo=FOO5

执行"/refresh"请求

BarService获取最新的配置:BAR5

allow-bean-definition-overriding

XConfiguration

@Configuration(Constants.CONFIG_BEAN_NAME)
@PropertySource(value = "file:" + Constants.CONFIG_PATH, ignoreResourceNotFound = true)
@Slf4j
public class XConfiguration {

    @Bean
    public BarService barService() {
        return new BarService();
    }

    @Bean
    public FooService fooService() {
        return new FooService();
    }

    @Bean
    public SingletonService singletonService() {
        return new SingletonService();
    }
}

spring 配置

spring:
  main:
    allow-bean-definition-overriding: true

ConfigRefreshEventListener

@Override
    public void onApplicationEvent(ConfigRefreshEvent event) {
        log.info("----------ConfigRefreshEventListener ConfigRefreshEvent Start----------");
        File file = new File(event.getPath());
        try (FileInputStream fileInputStream = new FileInputStream(file)) {
            Properties properties = new Properties();
            properties.load(fileInputStream);
            PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource("URL [file:" + CONFIG_PATH + "]", properties);
            //更新配置信息
            configurableEnvironment.getPropertySources().addFirst(propertiesPropertySource);
            applicationContext.setEnvironment(configurableEnvironment);


			//overriding config bean definition
            BeanDefinition xConfigurationBeanDefinition = applicationContext.getBeanDefinition(CONFIG_BEAN_NAME);
            applicationContext.registerBeanDefinition(CONFIG_BEAN_NAME, xConfigurationBeanDefinition);


            log.info("Properties dynamic update completed");
        } catch (Exception e) {
            log.error("ConfigRefreshEventListener Exception ", e);
        }
        log.info("----------ConfigRefreshEventListener ConfigRefreshEvent End----------");
    }

日志

按照下面步骤call请求:

  1. call “/refresh”
  2. 变更配置
  3. call “/refresh”

结论applicationContext.getBean(XXX.class) 配置的类全部会更新,且配置类无需添加@Scope("prototype")作用域。
`

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。