博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringBoot入坑指南之三:业务初始化
阅读量:6000 次
发布时间:2019-06-20

本文共 5340 字,大约阅读时间需要 17 分钟。

hot3.png

概述

在实际项目开发过程中,有时候需要在服务启动时进行一些业务初始化操作,这些操作只需要在服务启动后执行一次,那么通过Spring Boot如何实现该需求呢? Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口,这两种服务接口都可以实现上面的业务需求,本文将对这两种服务接口实现进行介绍。

ApplicationRunner与CommandLineRunner

异同点

  • 相同点

    • 两者均在服务启动完成后执行,并且只执行一次。
    • 两者都能获取到应用的命令行参数。
    • 两者触发执行的时间点是一致的。
  • 不同点

    • 虽然两者都是获取到应用的命令行参数,但是ApplicationRunner获取到的是封装后的ApplicationArguments对象,而CommandLine获取到的是ApplicationArguments中的sourceArgs属性(List<String>),即原始参数字符串列表.

执行顺序

很多误认为CommandLineRunner会先于ApplicationRunner执行,但是实际上两者是一起触发执行的,可以阅读SpringApplication.class方法中的源码

  • 1.SpringApplication.class中的run方法,会在执行完一些列初始化工作之后,调用callRunners方法执行Runner中的相关初始化代码。
public ConfigurableApplicationContext run(String... args) {        StopWatch stopWatch = new StopWatch();        stopWatch.start();        ConfigurableApplicationContext context = null;        Collection
exceptionReporters = new ArrayList(); this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(); Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment); context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); this.refreshContext(context); this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }
  • 2.在callRunners方法源码中,可以看出,ApplicationRunner和CommandLineRunner会被加载到同一个List中,之后排序并循环执行,所以并没有CommandLineRunner先执行一说。实际上,如果没有指定执行顺序,默认是ApplicationRunner先执行的。
private void callRunners(ApplicationContext context, ApplicationArguments args) {        List 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);            }        }    }

实现示例

1.实现Runner服务接口

  • 创建ApplicationRunnerFirst和ApplicationRunnerSecond两个类,实现ApplicationRunner接口。
@Component@Slf4jpublic class ApplicationRunnerFirst implements ApplicationRunner {    @Override    public void run(ApplicationArguments args) throws Exception {        log.info("This is {} Application Runner", "first");    }}
@Component@Slf4jpublic class ApplicationRunnerSecond implements ApplicationRunner {    @Override    public void run(ApplicationArguments args) throws Exception {        log.info("This is {} Application Runner", "second");    }}
  • 创建两个类,实现CommandLineRunner接口。
@Component@Slf4jpublic class CommandlineRunnerFirst implements CommandLineRunner {    @Override    public void run(String... args) throws Exception {        log.info("CommandLineRunner Args:{}",JSON.toJSONString(args));        log.info("This is {} Command Line Runner", "first");    }}
@Component@Slf4jpublic class CommandlineRunnerSecond implements CommandLineRunner {    @Override    public void run(String... args) throws Exception {        log.info("This is {} Command Line Runner", "second");    }}

2.指定执行顺序

可以使用org.springframework.core.annotation.Order注解设置执行顺序,其中数值越小越优先执行。例如:

@Component@Slf4j@Order(3)public class ApplicationRunnerFirst implements ApplicationRunner {    @Override    public void run(ApplicationArguments args) throws Exception {        log.info("ApplicationRunner Args:{}",JSON.toJSONString(args));        log.info("This is {} Application Runner", "first");    }}

分别将CommandlineRunnerSecond的Order设置为1,ApplicationRunnerSecond设置为2,ApplicationRunnerFirst设置为3,CommandlineRunnerFirst不设置。

3.运行结果

启动服务,运行结果如下:

启动日志

还有需注意的是,Runner的实现类必须注册为Spring Bean,否则不回被执行,阅读SpringApplication.run方法的源码就知道原因了。

示例源码

码云:

尾巴

无论ApplicationRunner还是CommandLineRunner,都是在应用启动完成后执行一次业务初始化代码,达到的效果也比较类似,由于ApplicationRunner的方法参数是ApplicationArguments对象,使用起来更加方便,所以更推荐使用。

转载于:https://my.oschina.net/centychen/blog/3007406

你可能感兴趣的文章
Testing - 软件测试知识梳理 - 理解测试
查看>>
L2TP/IPSec一键安装脚本
查看>>
android以json形式提交信息到服务器
查看>>
CetnOS 6.7安装Hive 1.2.1
查看>>
最短最优升级路径(完美世界2017秋招真题)
查看>>
【PHP基础】错误处理、异常处理
查看>>
Android之drawable state各个属性详解
查看>>
Linux——网段的划分,子网掩码,ABC类地址的表示法
查看>>
android开发(22)使用正则表达式 。从一个字符串中找出数字,多次匹配。
查看>>
AJAX
查看>>
2015 多校联赛 ——HDU5334(构造)
查看>>
几个ES6新特性
查看>>
mysql字符集
查看>>
DP_1d1d诗人小G
查看>>
非、半、结构化数据学习【转载】
查看>>
SpringMVC之单/多文件上传
查看>>
cache-fusion笔记
查看>>
改变div属性的一个实例
查看>>
avalon加载一闪而过现象
查看>>
线段树模板【数据结构 - 线段树】
查看>>