当前位置: 首页 > news >正文

如何在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里,也能实现类似缓存的效果。

相关文章:

  • BZOJ4584 : [Apio2016]赛艇
  • 查准考证网站卡了整整一个小时进不去,被抢票支配的恐惧又来了
  • 【C#公共帮助类】 ToolsHelper帮助类
  • 这次终于把Spring的监听器讲明白了
  • jquery ajax分页 js对象
  • 详解单例模式及其在Sping中的最优实践
  • Magento Shell
  • 深入了解ElasticSearch的Nested数据类型
  • Python3发送post请求,自动记住cookie
  • 基于SpringCloudGateway实现微服务网关
  • .NET框架
  • MongoDB快速上手,聊聊这款火了一阵又销声匿迹的非关系型数据库
  • 【2021总结】工作时间从865到995,这一年的变化真大
  • 十亿条数据需要每天计算怎么办?Spark快速入门
  • lvs持久性工作原理和配置
  • 【译】JS基础算法脚本:字符串结尾
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • angular2 简述
  • django开发-定时任务的使用
  • PHP 7 修改了什么呢 -- 2
  • React Transition Group -- Transition 组件
  • 高度不固定时垂直居中
  • 实现菜单下拉伸展折叠效果demo
  • 收藏好这篇,别再只说“数据劫持”了
  • 问题之ssh中Host key verification failed的解决
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 以太坊客户端Geth命令参数详解
  • ​虚拟化系列介绍(十)
  • "无招胜有招"nbsp;史上最全的互…
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • $.ajax()方法详解
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (附源码)php投票系统 毕业设计 121500
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转)大型网站的系统架构
  • (转载)Google Chrome调试JS
  • *** 2003
  • .net遍历html中全部的中文,ASP.NET中遍历页面的所有button控件
  • .Net转Java自学之路—SpringMVC框架篇六(异常处理)
  • @transactional 方法执行完再commit_当@Transactional遇到@CacheEvict,你的代码是不是有bug!...
  • [04]Web前端进阶—JS伪数组
  • [1181]linux两台服务器之间传输文件和文件夹
  • [Android Studio 权威教程]断点调试和高级调试
  • [C#C++]类CLASS
  • [c++] C++多态(虚函数和虚继承)
  • [C++进阶篇]STL中vector的使用
  • [CERC2017]Cumulative Code
  • [EFI]DELL XPS13 9360电脑 Hackintosh 黑苹果efi引导文件
  • [E单调栈] lc2487. 从链表中移除节点(单调栈+递归+反转链表+多思路)
  • [Flutter]打包IPA
  • [Gamma]阶段测试报告
  • [go 反射] 进阶
  • [iOS]让Xcode 4.2生成的app支持老的iOS设备(armv6)