这个问题说白了就是希望通过预加载数据,达到提升系统性能和响应速度的效果。像目前在很多场景中都有使用:

  • 电商平台的商品分类信息、用户基础资料:避免高并发时数据库被重复查询,降低响应延迟。
  • 系统参数配置(如地区编码、权限规则)、国际化资源:减少对配置中心或数据库的依赖,提升配置读取速度。
  • 促销活动的商品库存信息、新闻头条内容:通过预加载防止缓存击穿,应对突发流量。

题目说的是提前加载的redis缓存中,像配置类信息等这种变更频率低、实时性要求低的数据,还会加载到本地缓存中(如GuavaCacheCaffeine等),进一步减轻redis的压力,提升访问速度

重点

重点其实就是利用Spring 或 SpringBoot的扩展点来完成这部分功能

初始化数据加载触发机制

  1. 使用 CommandLineRunner或ApplicationRunner 在应用启动时自动执行数据加载逻辑。这是最常见的实现方式。
@Component
public class CacheWarmupRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
// 分页加载数据到缓存
PageHelper.startPage(1, 1000);
List<Product> products = productMapper.selectAll();
products.forEach(p -> redisTemplate.opsForHash().put("products", p.getId(), p));
}
}
  1. 使用 @PostConstruct 注解

    在服务类中通过 @Postconstruct 注解标记等初始化方法,在 Bean 创建后立即执行数据加载
@Service
public class CachePreloader {
@Autowired
private UserService userService;
@Autowired
private RedisTemplate<String, Object> redisTemplate; @PostConstruct
public void init() {
List<User> users = userService.getAllFixedData(); // 从数据库获取数据
users.forEach(user ->
redisTemplate.opsForValue().set("user:" + user.getId(), user)
);
}
}

结合缓存注解主动触发

使用 @Cacheable 注解:在首次调用方法时触发缓存写入,强制触发缓存写入。但需手动触发首次调用才能完成预加载

@Service
public class UserService {
@Cacheable(value = "users", key = "#root.methodName")
public List<User> getAllFixedData() {
return userRepository.findAll(); // 首次调用会写入缓存
}
}

注意

  • 推荐方案:使用 CommandlineRunner 或 @PostConstruct 在启动时主动加载数据到Redis,确保缓存立即可用。
  • 注解补充:@Cacheable 适用于懒加载场景,但需结合首次调用触发。
  • 注意事项:确保实体类实现 Serializable 接口,并正确配置 RedisTemplate 的序列化方式.

扩展知识

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

CommandLineRunner和ApplicationRunner

org.springframework.boot.CommandLineRunner

介绍

这两个是Springboot中新增的扩展点,之所以将这两个扩展点放在一起,是因为它两个功能特性高度相似,不同的只是名字、扩展方法形参数类型、执行先后的一些小的不同。

这两个接口触发时机为整个项目启动完毕后,自动执行。如果有多个CommandLineRunner,可以利用@Order来进行排序。

注意:

  • CommandLineRunner和ApplicationRunner都有一个扩展方法run(),但是run()形参数类型不同;
  • CommandLineRunner.run()方法的形参数类型是String... args,ApplicationRunner.run()的形参数类型是ApplicationArguments args;
  • CommandLineRunner.run()的执行时机要晚于ApplicationRunner.run()一点;
  • CommandLineRunner和ApplicationRunner触发执行时机是在Spring容器、Tomcat容器正式启动完成后,可以正式处理业务请求前,即项目启动的最后一步;
  • CommandLineRunner和ApplicationRunner可以应用的场景:项目启动前,热点数据的预加载、清除临时文件、读取自定义配置信息等;

使用场景

  1. 初始化数据:使用 CommandLineRunner 可以在应用启动后初始化一些必要的数据,例如从数据库加载某些配置或插入初始数据。
@Component
public class DataInitializer implements CommandLineRunner { @Override
public void run(String... args) {
System.out.println("初始化数据:插入初始数据");
// 模拟插入初始数据
insertInitialData();
} private void insertInitialData() {
System.out.println("插入数据:用户表初始数据");
}
}
  1. 启动后执行任务:使用 CommandLineRunner 可以在应用启动后执行一些特定的任务,比如发送一个通知或启动一些背景任务。
@Component
public class TaskExecutor implements CommandLineRunner { @Override
public void run(String... args) {
System.out.println("启动后执行任务:发送启动通知");
// 模拟发送启动通知
sendStartupNotification();
} private void sendStartupNotification() {
System.out.println("通知:应用已启动");
}
}
  1. 读取命令行参数:使用 CommandLineRunner 可以获取并处理命令行参数,这对于需要根据启动参数动态配置应用的场景非常有用。
@Component
public class CommandLineArgsProcessor implements CommandLineRunner { @Override
public void run(String... args) {
System.out.println("处理命令行参数:");
for (String arg : args) {
System.out.println("参数:" + arg);
}
}
} @SpringBootApplication
public class AppConfig {
public static void main(String[] args) {
SpringApplication.run(AppConfig.class, new String[]{"参数1", "参数2", "参数3"});
}
}

@PostConstruct

javax.annotation.PostConstruct

介绍

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

注意:

  • 使用@PostConstruct注解标记的方法不能有参数,除非是拦截器,可以采用拦截器规范定义的InvocationContext对象。
  • 使用@PostConstruct注解标记的方法不能有返回值,实际上如果有返回值,也不会报错,但是会忽略掉;
  • 使用@PostConstruct注解标记的方法不能被static修饰,但是final是可以的;

使用场景

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

InitializingBean

org.springframework.beans.factory.InitializingBean

介绍

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

注意:

  • 与InitializingBean#afterPropertiesSet()类似效果的是init-method,但是需要注意的是InitializingBean#afterPropertiesSet()执行时机要略早于init-method;
  • InitializingBean#afterPropertiesSet()的调用方式是在bean初始化过程中真接调用bean的afterPropertiesSet();
  • bean自定义属性init-method是通过java反射的方式进行调用 ;

使用场景

  1. 初始化资源:可以在 Bean 初始化后自动启动一些资源,如数据库连接、文件读取等。
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component; @Component
public class ResourceInitializer implements InitializingBean { @Override
public void afterPropertiesSet() {
// 模拟资源初始化
System.out.println("资源初始化:建立数据库连接");
} public void performAction() {
System.out.println("资源使用:执行数据库操作");
}
} @Configuration
@ComponentScan(basePackages = "com.seven")
public class AppConfig {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
ResourceInitializer initializer = context.getBean(ResourceInitializer.class);
initializer.performAction();
}
}
  1. 设置初始值
@Component
public class InitialValueSetter implements InitializingBean { private String initialValue; @Override
public void afterPropertiesSet() {
initialValue = "默认值";
System.out.println("设置初始值:" + initialValue);
} public void printValue() {
System.out.println("当前值:" + initialValue);
}
} @Configuration
@ComponentScan(basePackages = "com.seven")
public class AppConfig {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
InitialValueSetter valueSetter = context.getBean(InitialValueSetter.class);
valueSetter.printValue();
}
}
  1. 加载配置:可以在 Bean 初始化后加载必要的配置,如从文件或数据库中读取配置。
@Component
public class ConfigLoader implements InitializingBean { private String configValue; @Override
public void afterPropertiesSet() {
// 模拟配置加载
configValue = "配置值";
System.out.println("加载配置:" + configValue);
} public void printConfig() {
System.out.println("当前配置:" + configValue);
}
} @Configuration
@ComponentScan(basePackages = "com.seven")
public class AppConfig {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
ConfigLoader configLoader = context.getBean(ConfigLoader.class);
configLoader.printConfig();
}
}

面试官:SpringBoot 工程启动以后,希望将数据库中已有的固定内容提前加载到 Redis 缓存中,应该如何处理的更多相关文章

  1. SpringBoot程序启动时在Oracle数据库中建表充值

    例子工程下载链接:https://files.cnblogs.com/files/xiandedanteng/gatling20200428-1.zip 需求:在工程启动时在Oracle数据库中建表. ...

  2. Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(优先初始化Spring IOC容器)

    JavaWebSpringTomcatCache  最近用到在Tomcat服务器启动时自动加载数据到缓存,这就需要创建一个自定义的缓存监听器并实现ServletContextListener接口,并且 ...

  3. Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(例如顺序:1、初始化spring容器,2、初始化线程池,3、加载业务代码,将数据库中数据加载到内存中)

    最近公司要做功能迁移,原来的后台使用的Netty,现在要迁移到在uap上,也就是说所有后台的代码不能通过netty写的加载顺序加载了. 问题就来了,怎样让迁移到tomcat的代码按照原来的加载顺序进行 ...

  4. SpringBoot中各配置文件的优先级及加载顺序

    我们在写程序的时候会碰到各种环境(开发.测试.生产),因而,在我们切换环境的时候,我们需要手工切换配置文件的内容.这大大的加大了运维人员的负担,同时会带来一定的安全隐患. 为此,为了能更合理地重写各属 ...

  5. JAVAEE——SpringBoot配置篇:配置文件、YAML语法、文件值注入、加载位置与顺序、自动配置原理

    转载 https://www.cnblogs.com/xieyupeng/p/9664104.html @Value获取值和@ConfigurationProperties获取值比较   @Confi ...

  6. 错误: 找不到或无法加载主类(IDEA中启动spring boot项目)

    版权声明:本文为博主原创文章,如果转载请给出原文链接:http://www.jufanshare.com/content/142.html 提示:需要对IDEA编辑工具使用熟悉 出现一个问题,就是sp ...

  7. springboot 工程启动报错之Consider defining a bean of type ‘XXX’ in your configuration.

    一.前言: 使用springboot自动注入的方式搭建好了工程,结果启动的时候报错了!!!,错误如下图: Description: Field userEntityMapper in com.xxx. ...

  8. springboot工程启动时,报错:No bean named 'shiroFilter' available

    在启动Springboot项目时,报错:org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ' ...

  9. springboot工程启动即执行一段代码

    最近在做一个项目, 需要Tomcat启动后就执行一段代码 在这里需要用到CommandLineRunner这个接口, Spring boot的CommandLineRunner接口主要用于实现在应用初 ...

  10. 阿里面试官必问的12个MySQL数据库基础知识,哪些你还不知道?

    数据库基础知识 1.为什么要使用数据库 (1)数据保存在内存 优点: 存取速度快 缺点: 数据不能永久保存 (2)数据保存在文件 优点: 数据永久保存 缺点: 1)速度比内存操作慢,频繁的IO操作. ...

随机推荐

  1. C#生成目录

    在使用C#时,特别是做项目时,有时不会关注生成结果的目录结构的管理.但这样随着动态链接库的增多,文件夹中的文件数量会急剧增加.毕竟许多产品级的软件的清晰的目录是我们追求的目标. 通过以下简单的几步我们 ...

  2. uni-app在小程序上遇见的坑(a-b页面频繁跳转)

    功能描述 uni-app在小程序中. A页面跳B页面.B页面在调A页面.这样来回跳转. 多调几次,会有问题的. 出现的问题是,调不动了. 因为 这个方法是 uni.navigateTo 保留当前页.( ...

  3. Nodify学习 三:连接器

    前置 连接概述 连接是由两个点之间创建的.Source和Target依赖属性是Point类型,通常绑定到连接器的Anchor点. 基本连接 库中所有连接的基类是BaseConnection,它派生自S ...

  4. [记录点滴] 一个Python中实现flatten的方法

    之前如果想使用flatten,一般借助于numpy.ndarray.flatten. 但是 flatten只能适用于numpy对象,即array或者mat,普通的list列表不适用. 最近找到一个轻便 ...

  5. 上海站 | 2025 Seeed x LeRobot 具身智能黑客松报名开启!

    欢迎来到 Physical AI 的最前沿!Seeed x LeRobot 具身智能黑客松现邀请所有对在机器人领域训练模仿学习策略,并实时进行推理部署感兴趣的人,共同创造具有影响力的创新解决方案.在这 ...

  6. C++基础练习案例 - 模拟时钟系统 [DOS]

    大学时期初学C++,做了些案例练习,想着整理一下,供自己和网友翻阅参考,谢谢支持!有个[模拟时钟系统]做的还可以. [PS]存在一些小问题,如编写习惯等,文末有简单小结,请自行辨析.算是提供不良模板, ...

  7. 青岛oj集训1

    2025/3/4 内容:有向无环图(DAG) 优点:DAG有很多良好性质 拓扑排序 用处:可以根据拓扑序进行dp 这次计算所用的所有边的权值都是有计算过的 一张DAG图肯定有拓扑序(bfs序,dfs序 ...

  8. 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统

    我在业余时间开发了一款自己的独立产品:升讯威在线客服与营销系统.陆陆续续开发了几年,从一开始的偶有用户尝试,到如今线上环境和私有化部署均有了越来越多的稳定用户. 随时近来 AI 大模型的火热,越来越多 ...

  9. 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫

    目录 什么是 Selenium 环境搭建与配置 安装 Selenium 下载浏览器驱动 基础操作 启动浏览器并访问网页 定位网页元素 通过 ID 定位 通过 CSS 选择器定位 通过 XPath 定位 ...

  10. Ubuntu22.04 搭建Kubernetes 1.28版本集群

    依赖安装 准备工作需要在所有节点上进行. 安装 ssh 服务 安装 openssh-server sudo apt-get install openssh-server 修改配置文件 vim /etc ...