技巧!Spring Boot生产环境重新初始化Bean

文摘   2024-10-22 08:01   新疆  

最新实战案例锦集:《Spring Boot3实战案例合集》持续更新,每天至少更新一篇文章,订阅后将赠送文章最后展示的所有MD文档(学习笔记)。

环境:Spring Boot 3.2.5



1. 简介

在本篇文章中,我将介绍在运行时重新初始化单例 Spring Bean 的方法。默认情况下,具有单例作用域的 Spring Bean 不会在应用程序生命周期中重新初始化。不过,有时可能需要重新创建 Bean,例如在更新属性时。我将介绍几种实现此功能的方法。

2. 实战案例

为了演示我们将创建一个bean,该Bean将从配置文件中读取配置属性。如果文件中的属性发生变化,则对该Bean进行重新初始化以便得到最新的数据。

2.1 单例Bean定义

@Componentpublic class ConfigManager {
private static final Logger logger = LoggerFactory.getLogger(ConfigManager.class) ;
  private Map<StringObject> config = new HashMap<>() ;  // 配置的是具体值是绝对路径  private final String filePath ; public ConfigManager(@Value("${pack.app.filePath}") String filePath) { this.filePath = filePath ;    // 创建该bean对象时,加载配置文件信息    initConfig() ;  }  private void initConfig() { Properties properties = new Properties() ; try { properties.load(Files.newInputStream(Paths.get(filePath))) ; } catch (IOException e) {      logger.error("错误的加载配置文件, {}", e) ; } for (Map.Entry<Object, Object> entry : properties.entrySet()) { config.put(String.valueOf(entry.getKey()), entry.getValue()); }  } public Object getConfig(String key) { return config.get(key) ; }}

接下来,在classpath下新建config.properties配置文件,配置内容如下:

pack:  app:    filePathd:/pack/config.properties

下面我们可以定义一个Controller该测试当前的配置是否有问题。

@RestController@RequestMapping("/config")public class ConfigController {
@Autowired private ConfigManager configManager;
@GetMapping("/{key}") public Object get(@PathVariable String key) { return configManager.getConfig(key); }}

默认配置文件内容

title=xxxooo1

访问接口

目前,上面的接口不管配置如何修改,在不重启服务的情况下都无法得到最新的值;接下来我将通过几种方式来演示如何去刷新最新的配置。

2.2 通过公共方法刷新

如果我们想要重新加载属性而不是重新创建对象本身,我们可以简单地创建一个公共方法来再次初始化。在我们的ConfigManager中,让我们添加一个调用reloadConfig()方法的方法:

public void reloadConfig() {  initConfig() ;}

然后,当我们要重新加载属性时,就可以调用该方法。接着在Controller中定义另一个接口,该接口调用 reloadConfig()方法:

@GetMapping("/reloadConfig")public void reloadConfig() {  configManager.reloadConfig() ;}

通过测试上面的代码,你将得到最新的配置。此种方法也是最容易想到的办法。

2.3 使用@Lazy注解

我们可以使用@Lazy注解添加到注入的ConfigManager对象的地方,如下示例:

@Resource@Lazyprivate ConfigManager configManager;@Value("${pack.app.filePath}")private String filePath ;@GetMapping("/reinitializeBean")public void reinitializeBean() {  DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) context.getAutowireCapableBeanFactory() ;  // 销毁bean;销毁后当再次使用该bean时容器会再次执行整个创建过程  registry.destroySingleton("configManager") ;}

当配置发生变化后,先调用上面的/reinitializeBean接口,这会先把单例池中的实例删除,当再次调用/title接口时就会重新创建对象了。

2.4 通过容器获取Bean

我们可以将对应的bean销毁,然后在使用的时候再次从容器中获取,这时候由于已经将该单例bean销毁,单例池中已经没有了,所以会重新创建。

@Resourceprivate ApplicationContext context ;@Value("${pack.app.filePath}")private String filePath ;@GetMapping("/destroyBean")public void destroyBean() {  DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry) applicationContext.getAutowireCapableBeanFactory() ;  registry.destroySingleton("configManager") ;}

接下来修改使用获取数据的接口

@GetMapping("/{key}")public Object get(@PathVariable String key) {  ConfigManager cm = context.getBean(ConfigManager.class) ;  return cm.getConfig(key) ;}

如果配置文件修改后,我们先调用/destroyBean接口,这样当我们调用/title接口时,将会获取到最新的配置。

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

推荐文章

基于Spring Boot给所有Controller接口添加统一前缀的5种方式

提升性能:Java工程师必备的20条SQL最佳实践

Spring Boot 不同HTTP客户端 同步&异步请求对比

提升性能!彻底玩转缓存在Spring Boot中的各种应用技巧

Spring Boot 3声明式接口,完全可以替换OpenFeign

优雅!Spring Boot处理日志中的敏感数据

Spring Boot REST API版本控制的方案及选择

Jasypt轻松加密Spring Boot敏感数据

一文彻底玩转@RequestMapping,高级用法你未必知道

在SpringBoot项目中使用CompletableFuture优化并发REST调用的正确姿势

SpringBoot这些异常你知道原因吗?你遇过到几个?

SpringBoot异步任务@Async你真的会用吗?这6点细节你知道吗?

在SpringBoot中拦截修改请求Body的2种正确方式

太方便!Spring Boot整合Screw:高效生成数据库文档

优雅!SpringBoot通过函数式编程模型声明Restful API接口

总结7种JVM出现OOM时的原因及解决方案

Spring全家桶实战案例源码
spring, springboot, springcloud 案例开发详解
 最新文章