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

本文实例讲述了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

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 的区别从控制台我们就很了然了。

4. ApplicationRunner 与 CommandLineRunner 的区别

从上面的 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 启动项参数。进而也搞清楚了这两个接口之间的细微的区别。

更多关于java相关内容感兴趣的读者可查看本站专题:《Spring框架入门与进阶教程》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》

希望本文所述对大家java程序设计有所帮助。

(0)

相关推荐

  • spring boot2.0图片上传至本地或服务器并配置虚拟路径的方法

    最近写了关于图片上传至本地文件夹或服务器,上传路径到数据库,并在上传时预览图片.使用到的工具如下: 框架:spring boot 2.0 前端模板:thymeleaf 图片预览:js 首先,上传以及预览,js以及<input type="file">,以及预览图片的JS function Img(obj){ var imgFile = obj.files[0]; console.log(imgFile); var img = new Image(); var fr = ne

  • SpringBoot 2.0 整合sharding-jdbc中间件实现数据分库分表

    一.水平分割 1.水平分库 1).概念:  以字段为依据,按照一定策略,将一个库中的数据拆分到多个库中. 2).结果  每个库的结构都一样:数据都不一样:  所有库的并集是全量数据: 2.水平分表 1).概念  以字段为依据,按照一定策略,将一个表中的数据拆分到多个表中. 2).结果  每个表的结构都一样:数据都不一样:  所有表的并集是全量数据: 二.Shard-jdbc 中间件 1.架构图 2.特点 1).Sharding-JDBC直接封装JDBC API,旧代码迁移成本几乎为零. 2).适

  • spring boot2.0实现优雅停机的方法

    前期踩的坑 (spring boot 1.x) 1. 添加mavne依赖 <!-- springboot监控 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> 2. 启用shutdown 在配置文件里添加下面的配置 #

  • spring boot整合Swagger2的示例代码

    Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化RESTful风格的 Web 服务.总体目标是使客户端和文件系统作为服务器以同样的速度来更新.文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步.Swagger 让部署管理和使用功能强大的API从未如此简单. 1.代码示例 1).在pom.xml文件中引入Swagger2 <dependency> <groupId>io.springfox</groupId> <artifa

  • SpringBoot2.0 整合 Dubbo框架实现RPC服务远程调用方法

    一.Dubbo框架简介 1.框架依赖 图例说明: 1)图中小方块 Protocol, Cluster, Proxy, Service, Container, Registry, Monitor 代表层或模块,蓝色的表示与业务有交互,绿色的表示只对 Dubbo 内部交互. 2)图中背景方块 Consumer, Provider, Registry, Monitor 代表部署逻辑拓扑节点. 3)图中蓝色虚线为初始化时调用,红色虚线为运行时异步调用,红色实线为运行时同步调用. 4)图中只包含 RPC

  • springboot v2.0.3版本多数据源配置方法

    本篇分享的是springboot多数据源配置,在从springboot v1.5版本升级到v2.0.3时,发现之前写的多数据源的方式不可用了,捕获错误信息如: 异常:jdbcUrl is required with driverClassName. 先来说下之前的多数据源配置如: spring: datasource: url: jdbc:sqlserver://192.168.122.111;DatabaseName=flight username: sa password: 1234.abc

  • SpringBoot2.0整合Shiro框架实现用户权限管理的示例

    GitHub源码地址:知了一笑 https://github.com/cicadasmile/middle-ware-parent 一.Shiro简介 1.基础概念 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.作为一款安全框架Shiro的设计相当巧妙.Shiro的应用不依赖任何容器,它不仅可以在JavaEE下使用,还可以应用在JavaSE环境中. 2.核心角色 1)Subject:认证主体 代表当前系统的使用者,就是用户,在Shiro的认证中,

  • Spring Boot 2.0 设置网站默认首页的实现代码

    Spring Boot设置默认首页,方法实验OK如下 附上Application启动代码 /** * @ClassName Application * @Description Spring-Boot website启动类 * @author kevin.tian * @Date 2018-03 * @version 1.0.0 */ @SpringBootApplication @PropertySource(value={ "file:${APP_HOME_CONF}/overpayment

  • springboot2.0整合dubbo的示例代码

    写在前面: 使用springboot作为web框架,方便开发许多,做分布式开发,dubbo又不可少,那么怎么整合在一起呢, 跟我学一遍,至少会用 注意,springboot2.0和springboot1.x与dubbo整合不一样, 1.环境 1.新建一个空的maven项目,作为父工程,新建moudle,,service(接口层,及实现层,没有具体分,),web(web层,springboot项目) 项目结构如下 父pom如下 <properties> <project.build.sou

  • Spring Boot2.0 @ConfigurationProperties使用详解

    引言 Spring Boot的一个便捷功能是外部化配置,可以轻松访问属性文件中定义的属性.本文将详细介绍@ConfigurationProperties的使用. 配置项目POM 在pom.xml中定义Spring-Boot 为parent <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId>

  • SpringBoot2.0整合jackson配置日期格式化和反序列化的实现

    网上杂七杂八的说法不一,大多数都是抄来抄去,没有实践,近期在项目频繁遇到boot+jackson处理日期的问题,故开此贴. 首先是POM <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance

随机推荐