IT评测·应用市场-qidao123.com技术社区

标题: 面试官:SpringBoot 工程启动以后,希望将数据库中已有的固定内容提前加载到 Redis 缓存中,应该如那边理 [打印本页]

作者: 刘俊凯    时间: 2025-4-21 07:36
标题: 面试官:SpringBoot 工程启动以后,希望将数据库中已有的固定内容提前加载到 Redis 缓存中,应该如那边理
这个问题说白了就是希望通过预加载数据,达到提拔体系性能和响应速度的效果。像目前在很多场景中都有使用:
题目说的是提前加载的redis缓存中,像设置类信息等这种变更频率低、及时性要求低的数据,还会加载到当地缓存中(如GuavaCacheCaffeine等),进一步减轻redis的压力,提拔访问速度
重点

重点着实就是使用Spring 或 SpringBoot的扩展点来完成这部分功能
初始化数据加载触发机制

  1. @Component
  2. public class CacheWarmupRunner implements ApplicationRunner {
  3.     @Override
  4.     public void run(ApplicationArguments args) {
  5.         // 分页加载数据到缓存
  6.         PageHelper.startPage(1, 1000);
  7.         List<Product> products = productMapper.selectAll();
  8.         products.forEach(p -> redisTemplate.opsForHash().put("products", p.getId(), p));
  9.     }
  10. }
复制代码
  1. @Service
  2. public class CachePreloader {
  3.     @Autowired
  4.     private UserService userService;
  5.     @Autowired
  6.     private RedisTemplate<String, Object> redisTemplate;
  7.     @PostConstruct
  8.     public void init() {
  9.         List<User> users = userService.getAllFixedData(); // 从数据库获取数据
  10.         users.forEach(user ->
  11.             redisTemplate.opsForValue().set("user:" + user.getId(), user)
  12.         );
  13.     }
  14. }
复制代码
结合缓存注解主动触发

使用 @Cacheable 注解:在首次调用方法时触发缓存写入,强制触发缓存写入。但需手动触发首次调用才气完成预加载
  1. @Service
  2. public class UserService {
  3.     @Cacheable(value = "users", key = "#root.methodName")
  4.     public List<User> getAllFixedData() {
  5.         return userRepository.findAll(); // 首次调用会写入缓存
  6.     }
  7. }
复制代码
注意

扩展知识

关于Spring 和 SpringBoot的扩展点我已经写过一篇文章详细介绍,可以点击检察了解

CommandLineRunner和ApplicationRunner

org.springframework.boot.CommandLineRunner
介绍

这两个是Springboot中新增的扩展点,之所以将这两个扩展点放在一起,是因为它两个功能特性高度相似,不同的只是名字、扩展方法形参数范例、执行先后的一些小的不同。
这两个接口触发机会为整个项目启动完毕后,自动执行。如果有多个CommandLineRunner,可以使用@Order来举行排序。
注意:
使用场景

  1. @Component
  2. public class DataInitializer implements CommandLineRunner {
  3.     @Override
  4.     public void run(String... args) {
  5.         System.out.println("初始化数据:插入初始数据");
  6.         // 模拟插入初始数据
  7.         insertInitialData();
  8.     }
  9.     private void insertInitialData() {
  10.         System.out.println("插入数据:用户表初始数据");
  11.     }
  12. }
复制代码
  1. @Component
  2. public class TaskExecutor implements CommandLineRunner {
  3.     @Override
  4.     public void run(String... args) {
  5.         System.out.println("启动后执行任务:发送启动通知");
  6.         // 模拟发送启动通知
  7.         sendStartupNotification();
  8.     }
  9.     private void sendStartupNotification() {
  10.         System.out.println("通知:应用已启动");
  11.     }
  12. }
复制代码
  1. @Component
  2. public class CommandLineArgsProcessor implements CommandLineRunner {
  3.     @Override
  4.     public void run(String... args) {
  5.         System.out.println("处理命令行参数:");
  6.         for (String arg : args) {
  7.             System.out.println("参数:" + arg);
  8.         }
  9.     }
  10. }
  11. @SpringBootApplication
  12. public class AppConfig {
  13.     public static void main(String[] args) {
  14.         SpringApplication.run(AppConfig.class, new String[]{"参数1", "参数2", "参数3"});
  15.     }
  16. }
复制代码
@PostConstruct

javax.annotation.PostConstruct
介绍

可以看出来其本身不是Spring定义的注解,但是Spring提供了具体的实现。这个并不算一个扩展点,着实就是一个标注。其作用是在bean的初始化阶段,如果对一个方法标注了@PostConstruct,会先调用这个方法。这里重点是要关注下这个标准的触发点,这个触发点是在postProcessBeforeInitialization之后,InitializingBean.afterPropertiesSet之前。
注意:
使用场景

使用场景与 InitializingBean 类似,具体看下文
InitializingBean

org.springframework.beans.factory.InitializingBean
介绍

这个类,顾名思义,也是用来初始化bean的。InitializingBean接口为bean提供了初始化方法的方式,它只在bean实例化、属性注入后的提供了一个扩展点afterPropertiesSet方法,凡是继承该接口的类,在初始前、属性赋值后,都会执行该方法。这个扩展点的触发机会在postProcessAfterInitialization之前。

注意:
使用场景

  1. import org.springframework.beans.factory.InitializingBean;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class ResourceInitializer implements InitializingBean {
  5.     @Override
  6.     public void afterPropertiesSet() {
  7.         // 模拟资源初始化
  8.         System.out.println("资源初始化:建立数据库连接");
  9.     }
  10.     public void performAction() {
  11.         System.out.println("资源使用:执行数据库操作");
  12.     }
  13. }
  14.    
  15. @Configuration
  16. @ComponentScan(basePackages = "com.seven")
  17. public class AppConfig {
  18.     public static void main(String[] args) {
  19.         ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
  20.         ResourceInitializer initializer = context.getBean(ResourceInitializer.class);
  21.         initializer.performAction();
  22.     }
  23. }
复制代码
  1. @Component
  2. public class InitialValueSetter implements InitializingBean {
  3.     private String initialValue;
  4.     @Override
  5.     public void afterPropertiesSet() {
  6.         initialValue = "默认值";
  7.         System.out.println("设置初始值:" + initialValue);
  8.     }
  9.     public void printValue() {
  10.         System.out.println("当前值:" + initialValue);
  11.     }
  12. }
  13. @Configuration
  14. @ComponentScan(basePackages = "com.seven")
  15. public class AppConfig {
  16.     public static void main(String[] args) {
  17.         ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
  18.         InitialValueSetter valueSetter = context.getBean(InitialValueSetter.class);
  19.         valueSetter.printValue();
  20.     }
  21. }
复制代码
  1. @Component
  2. public class ConfigLoader implements InitializingBean {
  3.     private String configValue;
  4.     @Override
  5.     public void afterPropertiesSet() {
  6.         // 模拟配置加载
  7.         configValue = "配置值";
  8.         System.out.println("加载配置:" + configValue);
  9.     }
  10.     public void printConfig() {
  11.         System.out.println("当前配置:" + configValue);
  12.     }
  13. }
  14. @Configuration
  15. @ComponentScan(basePackages = "com.seven")
  16. public class AppConfig {
  17.     public static void main(String[] args) {
  18.         ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
  19.         ConfigLoader configLoader = context.getBean(ConfigLoader.class);
  20.         configLoader.printConfig();
  21.     }
  22. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4