• SpringBoot程序预装载数据


    简介

    在项目实际的开发过程中,有时候会遇到需要在应用程序启动完毕对外提供服务之前预先将部分数据装载到缓存的需求。本文就总结了常见的数据预装载方式及其实践。

    适用场景

    • 预装载应用级别数据到缓存:如字典数据、公共的业务数据
    • 系统预热
    • 心跳检测:如在系统启动完毕访问一个外服务接口等场景

    常见方式

    • ApplicationEvent
    • CommandLineRunner
    • ApplicationRunner

    ApplicationEvent

    应用程序事件,就是发布订阅模式。在系统启动完毕,向应用程序注册一个事件,监听者一旦监听到了事件的发布,就可以做一些业务逻辑的处理了。

    既然是发布-订阅模式,那么订阅者既可以是一个,也可以是多个。

    定义event

    
    import org.springframework.context.ApplicationEvent;
    public class CacheEvent   extends ApplicationEvent {
        public CacheEvent(Object source) {
            super(source);
        }
    }
    
    

    定义listener

    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;
    import org.springframework.util.CollectionUtils;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    @Slf4j
    @Component
    public class CacheEventListener implements ApplicationListener<CacheEvent> {
        @Autowired
        private MaskingService maskingService;
        @Autowired
        private RedisCache redisCache;
        @Override
        public void onApplicationEvent(CacheEvent cacheEvent) {
            log.debug("CacheEventListener-start");
            List<SysMasking> maskings = maskingService.selectAllSysMaskings();
            if (!CollectionUtils.isEmpty(maskings)) {
                log.debug("CacheEventListener-data-not-empty");
                Map<String, List<SysMasking>> cacheMap = maskings.stream().collect(Collectors.groupingBy(SysMasking::getFieldKey));
                cacheMap.keySet().forEach(x -> {
                    if (StringUtils.isNotEmpty(x)) {
                        log.debug("CacheEventListener-x={}", x);
                        List<SysMasking> list = cacheMap.get(x);
                        long count = redisCache.setCacheList(RedisKeyPrefix.MASKING.getPrefix() + x, list);
                        log.debug("CacheEventListener-count={}", count);
                    } else {
                        log.debug("CacheEventListener-x-is-empty");
                    }
                });
            } else {
                log.debug("CacheEventListener-data-is-empty");
            }
            log.debug("CacheEventListener-end");
        }
    }
    
    
    

    注册event

    
    @Slf4j
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
    public class BAMSApplication {
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(BAMSApplication.class, args);
            log.debug("app-started");
            context.publishEvent(new CacheEvent("处理缓存事件"));
        }
    }
    
    
    

    CommandLineRunner

    通过实现 CommandLineRunner 接口,可以在应用程序启动完毕,回调到指定的方法中。

    
    package com.ramble.warmupservice.runner;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    @Slf4j
    @Component
    public class CacheCommandLineRunner implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            log.debug("CacheCommandLineRunner-start");
            log.debug("CacheCommandLineRunner-参数={}", args);
            // 注入业务 service ,获取需要缓存的数据
            // 注入 redisTemplate ,将需要缓存的数据存放到 redis 中
            log.debug("CacheCommandLineRunner-end");
        }
    }
    
    
    

    ApplicationRunner

    同CommandLineRunner 类似,区别在于,对参数做了封装。

    
    package com.ramble.warmupservice.runner;
    import com.alibaba.fastjson.JSON;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.stereotype.Component;
    @Slf4j
    @Component
    public class CacheApplicationRunner implements ApplicationRunner {
        @Override
        public void run(ApplicationArguments args) throws Exception {
            log.debug("CacheApplicationRunner-start");
            log.debug("CacheApplicationRunner-参数={}", JSON.toJSONString(args));
            // 注入业务 service ,获取需要缓存的数据
            // 注入 redisTemplate ,将需要缓存的数据存放到 redis 中
            log.debug("CacheApplicationRunner-end");
        }
    }
    
    

    测试

    上述代码在idea中启动,若不带参数,输出如下:

    
    2022-04-28 15:44:00.981  INFO 1160 --- [           main] c.r.w.WarmupServiceApplication           : Started WarmupServiceApplication in 1.335 seconds (JVM running for 2.231)
    2022-04-28 15:44:00.982 DEBUG 1160 --- [           main] c.r.w.runner.CacheApplicationRunner      : CacheApplicationRunner-start
    2022-04-28 15:44:01.025 DEBUG 1160 --- [           main] c.r.w.runner.CacheApplicationRunner      : CacheApplicationRunner-参数={"nonOptionArgs":[],"optionNames":[],"sourceArgs":[]}
    2022-04-28 15:44:01.025 DEBUG 1160 --- [           main] c.r.w.runner.CacheApplicationRunner      : CacheApplicationRunner-end
    2022-04-28 15:44:01.025 DEBUG 1160 --- [           main] c.r.w.runner.CacheCommandLineRunner      : CacheCommandLineRunner-start
    2022-04-28 15:44:01.026 DEBUG 1160 --- [           main] c.r.w.runner.CacheCommandLineRunner      : CacheCommandLineRunner-参数={}
    2022-04-28 15:44:01.026 DEBUG 1160 --- [           main] c.r.w.runner.CacheCommandLineRunner      : CacheCommandLineRunner-end
    2022-04-28 15:44:01.026 DEBUG 1160 --- [           main] c.r.w.listener.CacheEventListener        : CacheEventListener-start
    2022-04-28 15:44:01.026 DEBUG 1160 --- [           main] c.r.w.listener.CacheEventListener        : CacheEventListener-参数=ApplicationEvent-->缓存系统数据
    2022-04-28 15:44:01.029 DEBUG 1160 --- [           main] c.r.w.listener.CacheEventListener        : CacheEventListener-end
    Disconnected from the target VM, address: '127.0.0.1:61320', transport: 'socket'
    Process finished with exit code 130
    
    
    

    若使用 java -jar xxx.jar --server.port=9009 启动,则输入如下:

    
    2022-04-28 16:02:05.327  INFO 9916 --- [           main] c.r.w.WarmupServiceApplication           : Started WarmupServiceApplication in 1.78 seconds (JVM running for 2.116)
    2022-04-28 16:02:05.329 DEBUG 9916 --- [           main] c.r.w.runner.CacheApplicationRunner      : CacheApplicationRunner-start
    2022-04-28 16:02:05.393 DEBUG 9916 --- [           main] c.r.w.runner.CacheApplicationRunner      : CacheApplicationRunner-参数={"nonOptionArgs":[],"optionNames":["server.port"],"sourceArgs":["--server.port=9009"]}
    2022-04-28 16:02:05.395 DEBUG 9916 --- [           main] c.r.w.runner.CacheApplicationRunner      : CacheApplicationRunner-end
    2022-04-28 16:02:05.395 DEBUG 9916 --- [           main] c.r.w.runner.CacheCommandLineRunner      : CacheCommandLineRunner-start
    2022-04-28 16:02:05.395 DEBUG 9916 --- [           main] c.r.w.runner.CacheCommandLineRunner      : CacheCommandLineRunner-参数=--server.port=9009
    2022-04-28 16:02:05.395 DEBUG 9916 --- [           main] c.r.w.runner.CacheCommandLineRunner      : CacheCommandLineRunner-end
    2022-04-28 16:02:05.395 DEBUG 9916 --- [           main] c.r.w.listener.CacheEventListener        : CacheEventListener-start
    2022-04-28 16:02:05.396 DEBUG 9916 --- [           main] c.r.w.listener.CacheEventListener        : CacheEventListener- 参数=ApplicationEvent-->缓存系统数据
    2022-04-28 16:02:05.396 DEBUG 9916 --- [           main] c.r.w.listener.CacheEventListener        : CacheEventListener-end
    
    
    

    执行顺序

    从上面测试的输出,可以看到三种方式执行的顺序为:
    ApplicationRunner--->CommandLineRunner--->ApplicationEvent

    另外,若同时定义多个runner,可以通过order来指定他们的优先级。

    代码

    https://gitee.com/naylor_personal/ramble-spring-cloud/tree/master/warmup-service

  • 相关阅读:
    python读写操作excel数据
    python读写操作excel数据小应用
    操作系统相关知识
    网络编程课堂笔记
    UDP简单初识+socketserver 模块实现服务端并发
    链接循环+模拟远程执行命令+实现大文件上传
    循环通信
    luogu P2761 软件补丁问题
    luogu P4016 负载平衡问题
    P3381 【模板】最小费用最大流(spfa板子)
  • 原文地址:https://www.cnblogs.com/Naylor/p/16203235.html
Copyright © 2020-2023  润新知