最新实战案例锦集:《Spring Boot3实战案例合集》持续更新,每天至少更新一篇文章,订阅后将赠送文章最后展示的所有MD文档(学习笔记)。
环境:Spring Boot 3.2.5
1. 简介
在生产环境中,实现不停机修改配置并实时生效的需求至关重要。该需求旨在确保系统能够持续提供服务,同时允许运维人员在不影响业务连续性的前提下,灵活调整系统配置。通过实现这一需求,运维人员可以更加高效地管理Spring Boot应用,提升系统的整体性能和稳定性。
如果你的项目使用到了Spring Cloud那么通过它提供的Actuator端点 /refresh 实现配置的动态刷新。
本篇文章将带你亲手实现配置的实时刷新功能,而无需借助 Spring Cloud。
我们将完成以下内容:
自定义作用域及注解 @RefreshScope
设计并实现一个自定义的作用域,使得带有 @RefreshScope 注解的 Bean 能够在配置更新时重新加载。
更新 application.yml 或 properties 配置文件
实现如何在配置文件的配置项发生变化后能被应用识别。
刷新带有 @RefreshScope 的 Bean
如何在配置更新后触发特定的事件,从而重新初始化带有 @RefreshScope 注解的 Bean,使其立即生效。
2. 实战案例
因为是自己手写实现,所以我们不需要引入任何其它的依赖。
2.1 自定义作用域
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Scope("refresh")
public @interface RefreshScope {
@AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
这里你可以不定义 proxyMode,而是在 @Scope 中直接定义好 proxyMode 属性值。不过那样不够灵活,如果需要自定义还得修改源码。
接下来,我们还需要定义自定义作用域 @RefreshScope 对应的 Scope 对象,因为最终获取使用该注解的bean 将会通过该Scope。
public class RefreshScope implements Scope, DisposableBean {
private final Logger logger = LoggerFactory.getLogger(getClass()) ;
private Map<String, BeanLifecycleWrapper> cache = new ConcurrentHashMap<>() ;
@Override
public Object get(String name, ObjectFactory<?> objectFactory) {
BeanLifecycleWrapper wrapper = cache.computeIfAbsent(name, key -> new BeanLifecycleWrapper(name, objectFactory)) ;
return wrapper.getBean() ;
}
@Override
public Object remove(String name) {
return cache.remove(name) ;
}
public void registerDestructionCallback(String name, Runnable callback) {
// TODO
}
public Object resolveContextualObject(String key) {
return key ;
}
public String getConversationId() {
return null ;
}
}
在解释上面代码前,我们应该先来了解下Bean的不同作用域是如何被创建的。