springboot 实现动态刷新配置的详细过程
作者:lfsun666
自定义的配置数据源,继承自Spring框架的 MapPropertySource 类
从一个名为 my.properties 的文件中读取配置信息,并在每10秒钟刷新一次。
这里不加@Component,是因为:
FilePropertiesSource filePropertiesSource = new FilePropertiesSource();
// 属性源是按照添加的顺序进行合并的,后添加的属性源中的属性会覆盖前面添加的属性源中的同名属性。
// 因此,为了确保我们自定义的属性源中的属性优先级最高,我们需要将它添加到属性源列表的最后。这样就能保证后添加的属性源中的属性值会覆盖之前的同名属性。
environment.getPropertySources().addLast(filePropertiesSource);
import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.MapPropertySource; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.util.StringUtils; import javax.annotation.PostConstruct; import java.io.*; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; /** * * 自定义的配置数据源,继承自Spring框架的 MapPropertySource 类,从一个名为 my.properties 的文件中读取配置信息,并在每10秒钟刷新一次。 * @author Administrator */ @Slf4j //@Component public class FilePropertiesSource extends MapPropertySource { private static final Logger logger = LoggerFactory.getLogger(FilePropertiesSource.class); private static final String CONFIG_FILE_NAME = "my.properties"; private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); public FilePropertiesSource() { super("filePropertiesSource", new HashMap<>()); } /** * 从配置文件中读取配置,10s 更新一次 */ @PostConstruct @Scheduled(fixedRate = 10_000) public void refreshSource() throws IOException { logger.info("开始读取配置文件 {}", CONFIG_FILE_NAME); InputStream inputStream = getClass().getClassLoader().getResourceAsStream(CONFIG_FILE_NAME); if (inputStream == null) { throw new FileNotFoundException("配置文件 " + CONFIG_FILE_NAME + " 不存在"); } Map<String, String> newProperties = new HashMap<>(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { line = line.trim(); if (StringUtils.isEmpty(line) || line.startsWith("#")) { continue; } String[] pair = StringUtils.split(line, "="); if (pair == null || pair.length != 2) { logger.warn("忽略配置项 {}", line); continue; } String key = pair[0].trim(); String value = pair[1].trim(); logger.debug("读取配置项 {} = {}", key, value); newProperties.put(key, value); } } catch (IOException e) { logger.error("读取配置文件 {} 出现异常:{}", CONFIG_FILE_NAME, e.getMessage(), e); throw e; } synchronized (this) { source.clear(); source.putAll(newProperties); } logger.info("读取配置文件完成,共读取 {} 个配置项,时间 {}", newProperties.size(), LocalDateTime.now().format(DATE_TIME_FORMATTER)); } /** * 覆盖 getProperty 方法,实现实时获取配置 * * @param key 配置项的 key * @return 配置项的值 */ @Override public Object getProperty(String key) { return source.get(key); } }
定义一个Spring配置类,定义了一个名为 filePropertiesSource 的Bean,并将其加入到环境变量中。
import com.lfsun.bootdynamicenv.config.FilePropertiesSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; /** * 一个Spring配置类,定义了一个名为 filePropertiesSource 的Bean,并将其加入到环境变量中。 */ @Configuration public class AutoConfig { @Bean public FilePropertiesSource filePropertiesSource(ConfigurableEnvironment environment) { FilePropertiesSource filePropertiesSource = new FilePropertiesSource(); // 属性源是按照添加的顺序进行合并的,后添加的属性源中的属性会覆盖前面添加的属性源中的同名属性。 // 因此,为了确保我们自定义的属性源中的属性优先级最高,我们需要将它添加到属性源列表的最后。这样就能保证后添加的属性源中的属性值会覆盖之前的同名属性。 environment.getPropertySources().addLast(filePropertiesSource); return filePropertiesSource; } }
为了方便直接用启动类作为控制层
@DependsOn(“filePropertiesSource”) 依赖于filePropertiesSource,以实现刷新配置。
@EnableScheduling 实现定时刷新
private String userName; 会被计算机名称覆盖,计算机名称优先级最高
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.DependsOn; import org.springframework.core.env.Environment; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @DependsOn("filePropertiesSource") @EnableScheduling @RestController @SpringBootApplication public class Application { @Autowired private Environment environment; @Value("${user-name}") private String userName; @GetMapping(path = "get") public String getProperty(String key) { return userName + " | " + environment.getProperty(key); } public static void main(String[] args) { SpringApplication.run(Application.class); } }
实现效果
访问:http://localhost:8080/get?key=my-user-id
可见修改user-name无效,被计算机名称所覆盖。
这时候修改my-user-id=
再访问:http://localhost:8080/get?key=my-user-id
已实现刷新:
ext:
@PostConstruct:
用于指定在依赖注入完成后需要执行的初始化方法。具体来说,当Spring容器完成对一个bean的依赖注入后,会调用该bean中使用了@PostConstruct注解的方法。
例如,如果在一个类中定义了一个方法,并在该方法上添加了@PostConstruct注解,那么当Spring容器初始化该类时,会自动调用该方法,以完成一些必要的初始化操作。
@PostConstruct注解的方法不能带有任何参数,也不能有返回值,因为Spring不会使用这些返回值。如果需要返回一些状态信息,可以使用类成员变量来保存这些信息。另外需要注意的是,@PostConstruct注解只能用于非静态方法。
@Scheduled(fixedRate = 10_000)
@Scheduled是一个Spring框架提供的注解,用于实现基于时间的任务调度。通过在方法上添加@Scheduled注解,并指定相应的属性,可以使得该方法在指定的时间间隔内自动执行。
例如,@Scheduled(fixedRate = 10_000)表示每隔10秒执行一次该方法。fixedRate属性指定了执行方法的时间间隔,单位为毫秒。除此之外,还可以使用cron表达式指定执行时间,如@Scheduled(cron = “0 0 12 * * ?”)表示每天中午12点执行一次该方法。
需要注意的是,使用@Scheduled注解的方法必须是无参方法,且返回类型为void或Future。如果需要传递参数或返回结果,可以使用实例变量或方法返回值进行传递。同时,为了使得@Scheduled注解生效,需要在启动类上面@EnableScheduling
到此这篇关于springboot 实现动态刷新配置的文章就介绍到这了,更多相关springboot动态刷新配置内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!