您现在的位置是:首页 >技术交流 >spring 热更新配置网站首页技术交流
spring 热更新配置
简介spring 热更新配置
我们经常会遇到不重启服务,自动刷新配置的需求。该如何实现呢?
@Scope(“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请求:
- call “/refresh”
- 变更配置
- call “/refresh”
结论: applicationContext.getBean(XXX.class)
配置的类全部会更新,且配置类无需添加@Scope("prototype")
作用域。
`
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。