SpringBoot源码解析(二十)ApplicationRunner

上篇文章介绍完SpringBoot内置tomcat的启动流程后,SpringBoot在Spring容器启动过程所做的扩展就大体介绍完了,本节继续SpringBoot的run方法,介绍一个比较有用的扩展点:ApplicationRunner

在这里插入图片描述

当然,在内置tomcat启动完成,到这行callRunners被调用,中间还经历了一些事件的发布,比如ApplicationStartedEvent,我们之前已经介绍过事件发布的原理,并详细分析了好几个事件了,它们发布的流程都是一致的,相信看到这里的朋友,已经很熟悉SpringBoot的事件发布机制,可以尝试自己去分析这几个事件的流程了

首先来看下ApplicationRunner接口的定义

@FunctionalInterface
public interface ApplicationRunner {
    void run(ApplicationArguments args) throws Exception;
}

它是一个函数式接口,只有一个run方法,实现这个接口后,在Spring容器启动完成之后,会回调run方法中的逻辑,我们看下效果

@SpringBootApplication
public class Application implements ApplicationRunner {

    public static void main(String[] args){
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("系统启动完成...");
    }
}

让SpringBoot的启动类实现该接口,在run方法中打印一行日志,启动项目就可以看到控制台里,tomcat启动成功之后打印了这行日志
在这里插入图片描述

接下来进入正题,看下callRunners方法的逻辑,需注意执行到这里,Spring容器的初始化流程已经完全结束了,它的第一个参数就是启动好的Spring容器,而第二个参数ApplicationArguments我们之前分析过,是对项目的启动参数String[] args的封装

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);
        }
    }
}

首先从容器里获取所有实现了ApplicationRunner接口和CommandLineRunner接口的bean,其中CommandLineRunner接口的定义跟ApplicationRunner类似

@FunctionalInterface
public interface CommandLineRunner {
    void run(String... args) throws Exception;
}

它们都只有一个run方法,只是这里CommandLineRunner接收的参数是String[] args,就是原始的启动参数,而ApplicationRunner接收的是我们传进来的封装过的ApplicationArguments

public interface ApplicationArguments {
    String[] getSourceArgs();
    Set<String> getOptionNames();
    boolean containsOption(String name);
    List<String> getOptionValues(String name);
    List<String> getNonOptionArgs();
}

其中的sourceArgs就是原始的String[] args,下面的各种option是对启动参数做了一个解析,比如- -port=8090等

在ApplicationRunner的实际使用中,很少会真的用到这些启动参数,大多数情况下,我们需要利用的是它的调用时机,所以在实际工作中,使用ApplicationRunner和使用CommandLineRunner区别不大

回到callRunners方法,从容器中找到了这些接口的实现类后,将它们存在同一个List中做了一个排序,所以这里也可以看到,ApplicationRunner和CommandLineRunner执行顺序并没有严格的前后之分,依赖其自身实现的排序接口

sort之后依次调了callRunner方法

private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
    try {
        runner.run(args);
    } catch (Exception var4) {
        throw new IllegalStateException("Failed to execute ApplicationRunner", var4);
    }
}

private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
    try {
        runner.run(args.getSourceArgs());
    } catch (Exception var4) {
        throw new IllegalStateException("Failed to execute CommandLineRunner", var4);
    }
}

两个callRunner方法都接收ApplicationArguments ,只是在commandLineRunner的实现中,调用run的时候从入参取出了原始的String[] args,最终调用了我们自己实现接口时定义的逻辑

这个扩展点比较简单,但却非常实用,我们经常会需要项目启动的时候做一些跟容器无关的初始化,比如加载一些外部网络资源等,就可以依赖这个接口实现

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值