如何在SpringBoot启动时执行初始化操作,两个简单接口就可以实现
Posted Java鱼仔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在SpringBoot启动时执行初始化操作,两个简单接口就可以实现相关的知识,希望对你有一定的参考价值。
(一)概述
最近遇到一个功能点,数据库中一张很简单的表有一千多条数据,这里的数据主要做到了值域映射的作用,简单来讲就是我可以通过中文名拿到数据库中对应的code值。原本的实现方式是每次用到之后去查一次sql,虽然不会有什么问题,但是只要是走了网络io,都会消耗时间。所以这个方案需要想办法优化。
优化的方式其实很简单,数据量不多,一千多条数据放在内存里也占不了多少空间。因此完全可以把一次性把数据加载到内存中,后面只需要每次去内存里调用就可以了。
(二)实现方案
方案想好了就要想实现方式了,想个最直接的方案,在Spring容器初始化时就把这些数据从数据库拿到内存中,后面就直接调用。
SpringBoot中有两个接口能实现该功能:CommandLineRunner和ApplicationRunner。
2.1 CommandLineRunner
首先了解一下CommandLineRunner的基本用法,CommandLineRunner可以在系统启动后执行里面的run方法
@Component
public class DataPrepare implements CommandLineRunner
@Override
public void run(String... args) throws Exception
System.out.println("CommandLineRunner执行数据初始化");
如果有多个类的话也可以通过@Order注解指定每个类的执行顺序。
接着就可以写代码的实现了,首先定义一个类用来将mysql的数据存到内存里,通过静态的Map存储
public class DataMap
public static Map<String, String> map = new HashMap<>();
public static void putData(String key, String value)
map.put(key, value);
public static String getDataByKey(String key)
return map.get(key);
接着在DataPrepare类中将数据都存入到静态到Map中。
@Component
public class DataPrepare implements CommandLineRunner
@Autowired
private DataMapper dataMapper;
@Override
public void run(String... args) throws Exception
//从数据库中取数据
List<DataDO> dataDOS = dataMapper.selectList(Wrappers.emptyWrapper());
//写入到DataMap中
dataDOS.forEach(item -> DataMap.putData(item.getName(), item.getCode()));
要使用到时候,只需要调用DataMap.getDataByKey()方法就可以直接使用了。
2.2 ApplicationRunner
ApplicationRunner和CommandLineRunner的功能十分相似,实现方式也基本相同。同样继承接口,并实现接口的run方法。
@Component
public class ApplicationDataPrepare implements ApplicationRunner
@Override
public void run(ApplicationArguments args) throws Exception
System.out.println("ApplicationRunner执行数据初始化");
在不指定@Order注解的情况下,ApplicationRunner会优先于CommandLineRunner执行。
两者的区别:
CommandLineRunner和ApplicationRunner的功能几乎是相同的,最大的区别在于两者run方法中的入参有所不同,CommandLineRunner通过String数组
来接收启动参数,而ApplicationRunner通过一个ApplicationArguments对象来接收。
在使用时,不管是String数组还是ApplicationArguments都可以拿到JVM的启动参数。
(三)源码分析
为什么通过实现一个接口,重写run方法就能达到启动程序后就自动执行代码的功能呢?我们可以通过SpringBoot的源码去看:
点进SpringApplication.run()方法,一直进入到public ConfigurableApplicationContext run(String… args)方法中,在执行完一系列初始化方法之后,执行了this.callRunners(context, applicationArguments)方法
callRunners的方法比较简单,首先定义了一个runners集合,并将需要执行的Bean放进去。可以看到ApplicationRunner和CommandLineRunner在这里被放入了runners中,接着对Order注解进行排序,最后遍历执行。
private void callRunners(ApplicationContext context, ApplicationArguments args)
List<Object> runners = new ArrayList();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
Iterator var4 = (new LinkedHashSet(runners)).iterator();
while(var4.hasNext())
Object runner = var4.next();
if (runner instanceof ApplicationRunner)
this.callRunner((ApplicationRunner)runner, args);
if (runner instanceof CommandLineRunner)
this.callRunner((CommandLineRunner)runner, args);
(四)总结
一个小小的细节可以节约多次的Sql调用。本章主要通过一个简单的例子引出ApplicationRunner和CommandLineRunner,实际在使用时也可以通过懒加载,在第一次使用时将数据塞到静态的Map里,也能实现类似缓存的效果。
以上是关于如何在SpringBoot启动时执行初始化操作,两个简单接口就可以实现的主要内容,如果未能解决你的问题,请参考以下文章
SpringBoot - 实现启动时执行指定任务(CommandLineRunnerApplicationRunner)