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

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

题目说的是提前加载的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. 首批!天翼云率先通过ITU国际标准认证!

    近日,天翼云通过国内唯一人工智能云平台领域的ITU国际标准评估--中国信通院组织的ITU-T F.AICP-GA人工智能云平台技术规范国际标准和<智算工程平台能力要求>国内标准一致性评估, ...

  2. 边缘计算与MEC浅谈

    本文分享自天翼云开发者社区<边缘计算与MEC浅谈>,作者:y****n 一.什么是边缘计算 边缘计算是在靠近物或数据源头的网络边缘侧,通过融合网络.计算.存储.应用核心能力的分布式开放平台 ...

  3. IPv6的优势分析

    本文分享自天翼云开发者社区<IPv6的优势分析>,作者:没烦恼 IPv6的优势分析 1.更大的地址空间 IPv6中IP地址的长度为128位,其地址容量则达到了2^128个,远远大于IPv4 ...

  4. DeepSeek部署本地知识库

    技术背景 在前面的两篇文章中,分别介绍过Ubuntu上关于DeepSeek的部署以及Windows平台关于DeepSeek的部署.其中内容包含了Ollama的下载安装和基本使用.DeepSeek模型文 ...

  5. Flink 部署和整体架构

    一.Flink运行部署模式和流程 部署模式: 1.Local 本地部署,直接启动进程,适合调试使用 2.Standalone Cluster集群部署,flink自带集群模式 3.On Yarn 计算资 ...

  6. Amis坑

    一.特殊字符 1.输入框.多行输入框输入$s字符默认替换成空. 如调用后端接口的输入框输入123$BB123,实际请求的参数为123,$后面的参数消息 解决办法:如确实需要输入$的话,在$前面加\即可 ...

  7. JUC并发—7.AQS源码分析三

    大纲 1.等待多线程完成的CountDownLatch介绍 2.CountDownLatch.await()方法源码 3.CountDownLatch.coutDown()方法源码 4.CountDo ...

  8. 离线环境安装nodejs及npm库i5ting_toc(超详细,手把手教学一通百通)

    一.离线环境先安装nodejs   1.在可联网的电脑上下载特定版本的 Node.js: 访问 Node.js 官方下载页面(https://nodejs.org/download/release/) ...

  9. Vigenere密码无密钥求解

    0.前言 最近摸了很长时间的鱼,然后最近突然想搞一个Vigenere密码的自动求解,花了不到一天来实现了一下这个东西,不过受限于自己的水平,没有搞的太难.当然,代码部分不是全部都是从 0 开始的,关于 ...

  10. 数据库离程序员有多远 - cnblogs救园行动感想

    这两周,我参与了博客园的"2024救园行动",成了终身会员.说实话,当初报名的时候,我心里还挺兴奋的,想着这下能和不少老朋友在这个社区里再次相聚.毕竟,在数据库行业摸爬滚打了这么多 ...