这个问题说白了就是希望通过预加载数据,达到提升系统性能和响应速度的效果。像目前在很多场景中都有使用:
题目说的是提前加载的redis缓存中,像配置类信息等这种变更频率低、实时性要求低的数据,还会加载到本地缓存中(如GuavaCache,Caffeine等),进一步减轻redis的压力,提升访问速度
重点其实就是利用Spring 或 SpringBoot的扩展点来完成这部分功能
@Component
public class CacheWarmupRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
// 分页加载数据到缓存
PageHelper.startPage(1, 1000);
List products = productMapper.selectAll();
products.forEach(p -> redisTemplate.opsForHash().put("products", p.getId(), p));
}
}
@Service
public class CachePreloader {
@Autowired
private UserService userService;
@Autowired
private RedisTemplate redisTemplate;
@PostConstruct
public void init() {
List 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 getAllFixedData() {
return userRepository.findAll(); // 首次调用会写入缓存
}
}
关于Spring 和 SpringBoot的扩展点我已经写过一篇文章详细介绍,可以点击查看了解
org.springframework.boot.CommandLineRunner
这两个是Springboot中新增的扩展点,之所以将这两个扩展点放在一起,是因为它两个功能特性高度相似,不同的只是名字、扩展方法形参数类型、执行先后的一些小的不同。
这两个接口触发时机为整个项目启动完毕后,自动执行。如果有多个CommandLineRunner
,可以利用@Order
来进行排序。
注意:
CommandLineRunner
可以在应用启动后初始化一些必要的数据,例如从数据库加载某些配置或插入初始数据。@Component
public class DataInitializer implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("初始化数据:插入初始数据");
// 模拟插入初始数据
insertInitialData();
}
private void insertInitialData() {
System.out.println("插入数据:用户表初始数据");
}
}
CommandLineRunner
可以在应用启动后执行一些特定的任务,比如发送一个通知或启动一些背景任务。@Component
public class TaskExecutor implements CommandLineRunner {
@Override
public void run(String... args) {
System.out.println("启动后执行任务:发送启动通知");
// 模拟发送启动通知
sendStartupNotification();
}
private void sendStartupNotification() {
System.out.println("通知:应用已启动");
}
}
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"});
}
}
javax.annotation.PostConstruct
可以看出来其本身不是Spring定义的注解,但是Spring提供了具体的实现。这个并不算一个扩展点,其实就是一个标注。其作用是在bean的初始化阶段,如果对一个方法标注了@PostConstruct
,会先调用这个方法。这里重点是要关注下这个标准的触发点,这个触发点是在postProcessBeforeInitialization
之后,InitializingBean.afterPropertiesSet
之前。
注意:
使用场景与 InitializingBean 类似,具体看下文
org.springframework.beans.factory.InitializingBean
这个类,顾名思义,也是用来初始化bean的。InitializingBean
接口为bean提供了初始化方法的方式,它只在bean实例化、属性注入后的提供了一个扩展点afterPropertiesSet
方法,凡是继承该接口的类,在初始前、属性赋值后,都会执行该方法。这个扩展点的触发时机在postProcessAfterInitialization
之前。
注意:
init-method
,但是需要注意的是InitializingBean#afterPropertiesSet()执行时机要略早于init-method;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();
}
}
@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();
}
}
@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();
}
}
本文来自在线网站:seven的菜鸟成长之路,作者:seven,转载请注明原文链接:www.seven97.top
参与评论
手机查看
返回顶部