ApplicationRunner、CommandLineRunner 接口源码跟踪

十、ApplicationRunner、CommandLineRunner 接口

ApplicationRunner和CommandLineRunner 是SpringBoot提供的接口,实现这两个接口中任意一个接口,并且配置给spring容器管理的bean,重写这两个接口的方法,将会被回调。

public class SpringApplication {
	/**
     * Run the Spring application, creating and refreshing a new
     * {@link ApplicationContext}.
     * @param args the application arguments (usually passed from a Java main method)
     * @return a running {@link ApplicationContext}
     */
    public ConfigurableApplicationContext run(String... args) {
       StopWatch stopWatch = new StopWatch();
       stopWatch.start();
       ConfigurableApplicationContext context = null;
       Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
       configureHeadlessProperty();
        // 使用spi机制加载SpringApplicationRunListener.class, 获取这个类的实例
       SpringApplicationRunListeners listeners = getRunListeners(args);
       listeners.starting();
       try {
           // 封装请求参数
          ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
          ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
          configureIgnoreBeanInfo(environment);
           // 打印banner
          Banner printedBanner = printBanner(environment);
           // 创建ApplicationContext对象, 此过程中完成类型推断
          context = createApplicationContext();
           // 使用spi机制加载SpringBootExceptionReporter.class, 获取这个类的实例
          exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
          prepareContext(context, environment, listeners, applicationArguments, printedBanner);
           // 刷新ApplicationContext, 即刷新spring容器
          refreshContext(context);
           // 空实现, 受保护的方法
          afterRefresh(context, applicationArguments);
          stopWatch.stop();
          if (this.logStartupInfo) {
             new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), 
                                                                         stopWatch);
          }
           // 回调所有listener的started方法, 
           // 这个listener就是前面提到的由spi机制加载的SpringApplicationRunListeners的实例
          listeners.started(context);
           // 【在此处, 回调spring容器中所有的runner】
           // 【即Spring容器中任何实现了ApplicationRunner和CommandLineRunner这两个接口的bean】
           // 【将会在此处回调它们重写的这两个接口的方法】
           //  下面进去看看这个方法, 注意个细节context传进去了
          callRunners(context, applicationArguments);// <---------------回调方法,进去看看
       }
       catch (Throwable ex) {
          handleRunFailure(context, ex, exceptionReporters, listeners);
          throw new IllegalStateException(ex);
       }

       try {
           // 回调所有listener的running方法, 
           // 这个listener就是前面提到的由spi机制加载的SpringApplicationRunListeners的实例
          listeners.running(context);
       }
       catch (Throwable ex) {
          handleRunFailure(context, ex, exceptionReporters, null);
          throw new IllegalStateException(ex);
       }
       return context;
    }
}



// 同样是在SpringApplication的这个类中,调用的方法
public class SpringApplication {    
    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        
        // 到这里来就已经很明显了, 它从传过来的context, 
        // 即从spring容器中拿到了实现ApplicationRunner接口的bean
        //                   和实现CommandLineRunner接口的bean
        
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        
        // 很好, 这里来了个排序, 
        // 这个排序可以使用@Order注解的value值指定, 值越大,优先级越低。默认是Ordered.LOWEST_PRECEDENCE[最低优先级]
        //                                     具体可以看org.springframework.core.Ordered这个接口
        //                                     @Order注解仅仅用于控制组件的加载顺序,不能控制注入优先级
        // 或者由@Priority注解的value值来指定, 值通常不设置为负数 , 值越小, 优先级越高
        //     可以用来处理同一个接口下有多个实现的bean, 那么优先注入哪个bean可以用Priority指定,效果有点像@Primary
        //     可以控制注入的优先级
        AnnotationAwareOrderComparator.sort(runners);
        
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);//<<-----------回调所有ApplicationRunner接口
                                                             //             实现bean重写的run方法
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);//<<-----------回调所有CommandLineRunner接口
                                                             //             实现bean重写的run方法
            }
        }
    }
    
    // 回调就在下面, 这两个接口唯一差别就是回调的顺序, ApplicationRunner先, CommandLineRunner靠后
    // 其实还有一个问题指出一下, 在 refreshContext(context);这步, 就相当于已经把我们自己配置的bean实例化放到容器中了
    // 所以到callRunners(context, applicationArguments);这步, 从容器中拿到接口的实现bean是没有问题的
    private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
		try {
			(runner).run(args);
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
		}
	}

	private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
		try {
			(runner).run(args.getSourceArgs());
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值