原创 Java技术前沿 Java技术前沿 2024年09月14日 08:30 河北
Java技术前沿
专注分享Java技术,包括但不限于 SpringBoot,SpringCloud,Docker,消息中间件等。
79篇原创内容
公众号
引言
在许多实际的应用场景中,动态管理应用程序的配置显得尤为重要。特别是在微服务架构的复杂环境中,由于业务需求、操作变更或外部条件的变化,不同的服务常常需要实现配置的实时更新。应用程序可能需要根据用户所处的具体环境、外部API的实时数据或遵循不断变化的业务需求来灵活调整其行为。传统的application.properties
文件因其静态特性,在应用程序不重启的情况下难以进行配置修改,这限制了系统的灵活性和响应速度。
幸运的是,Spring Boot框架为我们提供了多种高效且强大的解决方案,使得在运行时动态调整配置成为可能,而无需中断应用的运行。这些能力使得在实时应用中实现功能开关的快速切换、动态更新与第三方服务相关的配置等变得简单直接,从而极大地提升了应用的灵活性和用户体验。
一、代码示例
1. 定义一个 作用域为 Prototype 类型的 Bean
当我们需要在不干扰已创建的Bean实例状态,同时也不改变全局应用程序配置的情况下,动态地调整特定Bean的某些属性时,直接将带有@Value注解的属性注入到@Service类或其他组件中是不够灵活的。这是因为这些属性在Spring应用程序上下文的生命周期中被视为静态的,即它们的注入过程是在容器初始化阶段完成的,并且是一次性的。一旦Bean被创建并注入其依赖项,这些属性的值就固定了,除非通过重启应用或特定的更新机制,否则它们不会随外部变化而更新。
因此,为了实现属性的动态调整,我们需要探索其他机制,如使用Spring Cloud Config、Spring的@RefreshScope注解(针对支持动态刷新的配置源),或者编写自定义的Bean监听器或配置更新服务,以响应外部事件并更新Bean的属性值。这些方法允许我们在不中断服务的情况下,根据需要动态地调整Bean的属性。
@Configurationpublic class AppConfig {
@Bean // 声明为多例bean作用域
@Scope("prototype")
public UserService userService(@Value("${pack.app.title:}") String title) {
return new UserService(title) ;
}
}
public class UserService {
private final String title;
public UserService(String title) {
this.title= title;
} // getters, setters}
利用 @Scope 将 UserService 生命为一个多例的作用域,可保证每次 getBean 时 都能拿到的是一个新的对象。每次都会重新注入title值
例如:
@RestController
public class UserController {
@Resource private ApplicationContext context;
@Resource private ApplicationContext context ;
@GetMapping("/update")
public String update() {
// 设置系统属性值;
// 注意你不能吧属性定义在application.yml配置文件中
System.setProperty("pack.app.title", "xxxooo - " + new Random().nextInt(10000)) ;
return "update success" ;
}
@GetMapping("/title")
public String title() {
return this.context.getBean("us", UserService.class).getTitle() ;
}
}
2.使用@RefreshScope
我们可以利用Spring Cloud的@RefreshScope注解以及/actuator/refresh端点来实现配置的动态更新。这种方式的核心机制在于,它会将标记了@RefreshScope的Bean封装成代理对象。当通过/actuator/refresh端点触发配置刷新时,Spring Cloud会重新解析这些Bean的依赖配置(特别是那些通过外部配置中心如Spring Cloud Config管理的配置),并重新获取Bean的实例(实际上是重新获取这些代理对象所代理的真实Bean实例)。这样,每次通过代理对象访问Bean时,如果配置已更新,则会反映最新的配置值,从而实现了配置的实时更新。
此方法与前面提到的在Spring Boot中动态调整Bean属性的需求相契合,但更加侧重于通过Spring Cloud的生态系统来实现配置的集中管理和动态刷新。通过使用@RefreshScope,开发者可以更容易地实现微服务架构中配置的动态调整,而无需重启整个应用程序。
pom.xml文件引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置文件中开启refresh接口
management:
endpoint:
refresh:
enabled: true
endpoints:
web:
exposure:
include: refresh
配置完成后 定义需要实时刷新的bean对象了
@RefreshScope
@Component
public class AppComponent {
@Value("${pack.app.title:}")
private String title ;
public String getTitle() {
return this.title ;
}
}
接下来动态向Environment中添加PropertySource该对象中存入的是我们需要动态刷新的值。
@Service
public class PackPropertyService {
private static final String PACK_PROPERTIES_SOURCE_NAME = "packDynamicProperties" ;
private final ConfigurableEnvironment environment ;
public PackPropertyService(ConfigurableEnvironment environment) {
this.environment = environment ;
}
// 更新或者添加PropertySource操作
public void updateProperty(String key, String value) {
MutablePropertySources propertySources = environment.getPropertySources() ;
if (!propertySources.contains(PACK_PROPERTIES_SOURCE_NAME)) {
Map<String, Object> properties = new HashMap<>() ;
properties.put(key, value) ;
propertySources.addFirst(new MapPropertySource(PACK_PROPERTIES_SOURCE_NAME, properties)) ;
}
else {
// 替换更新值
MapPropertySource propertySource = (MapPropertySource) propertySources.get(PACK_PROPERTIES_SOURCE_NAME) ;
propertySource.getSource().put(key, value) ;
}
}
}
接口测试
@RestController
@RequestMapping("/configprops")
public class PropertyController {
private final PackPropertyService pps ;
private final AppComponent app ;
public PropertyController(PackPropertyService pps, AppComponent app) {
this.pps = pps ; this.app = app ;
}
@PostMapping("/update")
public String updateProperty(String key, String value) {
pps.updateProperty(key, value) ; return "update success" ;
}
@GetMapping("/title")
public String title() {
return app.getTitle() ;
}
}
Java技术前沿
专注分享Java技术,包括但不限于 SpringBoot,SpringCloud,Docker,消息中间件等。
79篇原创内容
公众号
java93
springboot82
java · 目录
上一篇SpringBoot实战:如何实现多端口监听下一篇AES加解密算法的原理和应用与安全性解析