Spring Boot 2 实战:自定义启动运行逻辑

Stella981
• 阅读 504

Spring Boot 2 实战:自定义启动运行逻辑

1. 前言

不知道你有没有接到这种需求,项目启动后立马执行一些逻辑。比如缓存预热,或者上线后的广播之类等等。可能现在没有但是将来会有的。想想你可能的操作, 写个接口上线我调一次行吗?NO!NO!NO!这种初级菜鸟才干的事。今天告诉你个骚操作使得你的代码更加优雅,逼格更高。

2. CommandLineRunner 接口

 package org.springframework.boot; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; /**  * Interface used to indicate that a bean should <em>run</em> when it is contained within  * a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined  * within the same application context and can be ordered using the {@link Ordered}  * interface or {@link Order @Order} annotation.  * <p>  * If you need access to {@link ApplicationArguments} instead of the raw String array  * consider using {@link ApplicationRunner}.  *  * @author Dave Syer  * @see ApplicationRunner  */ @FunctionalInterface public interface CommandLineRunner {     /**      * Callback used to run the bean.      * @param args incoming main method arguments      * @throws Exception on error      */     void run(String... args) throws Exception; }

CommandLineRunner 作用是当springApplication 启动后,在同一应用上下文中定义的多个 CommandLineRunner 类型的 Spring Bean 按照标记顺序执行。如果你想替代以数组方式接收 args 参数 可以用 另一个接口代替 org.springframework.boot.ApplicationRunner

talk is cheap show your code 下面我就来操作一波演示一下。

2.1 优先级比较高的 CommandLineRunner 实现

 package cn.felord.begin; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; /**  * 优先级比较高 通过实现接口{@link Ordered}的方式 来指定优先级  *  命令行测试参数     --foo=bar --dev.name=码农小胖哥  java,springboot  * @author Felordcn  * @since 2019/6/17 23:06  */ @Slf4j @Component public class HighOrderCommandLineRunner implements CommandLineRunner , Ordered {     @Override     public void run(String... args) throws Exception {       log.info("i am  highOrderRunner");     }     @Override     public int getOrder() {         return Ordered.HIGHEST_PRECEDENCE;     } }

2.2 优先级比较低的 CommandLineRunner 实现:

 package cn.felord.begin; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /**  * 优先级比较低 通过注解{@link Order}方式来指定优先级  * 比最优大64 说明会在 {@link HighOrderCommandLineRunner} 之后执行  *  * @author Felord  * @since 2019/6/17 23:07  */ @Slf4j @Order(Ordered.HIGHEST_PRECEDENCE + 64) @Component public class LowOrderCommandLineRunner implements CommandLineRunner {     @Override     public void run(String... args) throws Exception {         log.info("iamlowOrderRunner");     } }

2.3 用 ApplicationRunner 实现最低优先级:

 package cn.felord.begin; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.List; /**  * 优先级最低的实现  * @author Felordcn  * @since 2019/6/18 22:13  */ @Slf4j @Component public class DefaultApplicationRunner implements ApplicationRunner, Ordered {     @Override     public void run(ApplicationArguments args) throws Exception {         log.info("iamapplicationRunner");     }     @Override     public int getOrder() {         return Ordered.HIGHEST_PRECEDENCE+65;     } }

启动springboot 后控制台打印出了执行结果:

 2019-11-02 21:18:14.603  INFO 10244 --- [           main] c.f.begin.HighOrderCommandLineRunner   : i am  highOrderRunner 2019-11-02 21:18:14.604  INFO 10244 --- [           main] c.f.begin.LowOrderCommandLineRunner    : i am  lowOrderRunner 2019-11-02 21:18:14.604  INFO 10244 --- [           main] c.f.begin.DefaultApplicationRunner     : i am applicationRunner

3. 进阶操作 —— 读取通过Spring Boot命令行启动注入的参数

达到我们开篇的期望结果。那么这两个接口啥区别呢? Spring 官方不会吃饱了没事干弄两个这来折腾人,应该是有区别的,根据接口方法 run 方法可以看出来参数都不一样,额外科普一下 Spring Boot 如何传递额外参数通过命令行 执行 java -jar 传递给 main 方法,规则如下

  • 键值对 格式为 --K=V 多个使用空格隔开

  • 值 多个空格隔开 在idea 开发工具中打开main方法配置项,进行如下配置,其他ide工具同理。参数内容为:

    --foo=bar --dev.name=码农小胖哥 java springboot

Spring Boot 2 实战:自定义启动运行逻辑

HighOrderCommandLineRunner 打印一下 args 参数:

 package cn.felord.begin; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; /**  * 优先级比较高 通过实现接口{@link Ordered}的方式 来指定优先级  *  命令行测试参数     --foo=bar --dev.name=码农小胖哥  java,springboot  * @author dax  * @since 2019/6/17 23:06  */ @Slf4j @Component public class HighOrderCommandLineRunner implements CommandLineRunner , Ordered {     @Override     public void run(String... args) throws Exception {         for (String arg : args) {             System.out.println("arg = " + arg);         }       log.info("i am  highOrderRunner");     }     @Override     public int getOrder() {         return Ordered.HIGHEST_PRECEDENCE;     } }

然后 DefaultApplicationRunnerApplicationArguments 我们也一探究竟:

 package cn.felord.begin; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.List; /**  * @author Felord  * @since 2019/6/18 22:13  */ @Slf4j @Component public class DefaultApplicationRunner implements ApplicationRunner, Ordered {     @Override     public void run(ApplicationArguments args) throws Exception {         log.info("i am applicationRunner");         args.getOptionNames().forEach(System.out::println);         System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");         String[] sourceArgs = args.getSourceArgs();         if (sourceArgs!=null){             for (String sourceArg : sourceArgs) {                 System.out.println("sourceArg = " + sourceArg);             }         }         System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");         List<String> foo = args.getOptionValues("foo");         if (!CollectionUtils.isEmpty(foo)){             foo.forEach(System.out::println);         }         System.out.println("++++++++++++");         List<String> nonOptionArgs = args.getNonOptionArgs();         System.out.println("nonOptionArgs.size() = " + nonOptionArgs.size());         nonOptionArgs.forEach(System.out::println);     }     @Override     public int getOrder() {         return Ordered.HIGHEST_PRECEDENCE+65;     } }

重新启动 Spring Boot 控制台打印出了结果:

 arg = --foo=bar arg = --dev.name=码农小胖哥 arg = java arg = springboot 2019-11-02 21:18:14.603  INFO 10244 --- [           main] c.f.begin.HighOrderCommandLineRunner   : i am  highOrderRunner 2019-11-02 21:18:14.604  INFO 10244 --- [           main] c.f.begin.LowOrderCommandLineRunner    : i am  lowOrderRunner 2019-11-02 21:18:14.604  INFO 10244 --- [           main] c.f.begin.DefaultApplicationRunner     : i am applicationRunner dev.name foo >>>>>>>>>>>>>>>>>>>>>>>>>> sourceArg = --foo=bar sourceArg = --dev.name=码农小胖哥 sourceArg = java sourceArg = springboot <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< bar ++++++++++++ nonOptionArgs.size() = 2 java springboot

我们发现可以利用这两个接口来读取 Spring Boot 命令行参数。其实我们还可以使用 @Value 注解来读取,这里不进行讲解,有兴趣可以自己尝试。到这里 ApplicationRunnerCommandLineRunner 的区别从控制台我们就很了然了。

  1. ApplicationRunnerCommandLineRunner 的区别

从上面的 log 我们知道 arg=CommandLineRunnerargs数组打印,仅仅单纯把上面的参数以空格为规则解析成了原汁原味的数组。而 ApplicationRunner 则更加精细化。通过打印可以知道 ApplicationArguments 提供了一些很有用的参数解析方法:

  • args.getOptionNames() 是获取键值对 --K=V 中的 K

  • args.getOptionValues("foo") 用来通过 K 来获取键值对的值 V

  • args.getSourceArgs() 等同于 CommandLineRunnerargs 数组

  • args.getNonOptionArgs() 最惨用来获取单身狗

要注意的是 解析 ApplicationArguments 时要处理空指针问题。

5. 总结

今天我们通过对 CommandLineRunnerApplicationRunner 讲解。解决了如何在 Spring Boot 启动时执行一些逻辑的问题以及如何来编排多个启动逻辑的优先级顺序。同时我们进阶一步,通过这两个方法读取 Spring Boot 启动项参数。进而也搞清楚了这两个接口之间的细微的区别。希望对你有所帮助。

Spring Boot 2 实战:自定义启动运行逻辑

Spring Boot 2 实战:自定义启动运行逻辑

本文分享自微信公众号 - 码农小胖哥(Felordcn)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这