CommandLineRunner、ApplicationRunner of SpringBoot

如果需要在容器启动的时候就开始执行一些内容。比如配置初始化等,SpringBoot提供的这个接口就是干这个的。同时,通过其源码发现,它还提供了另外一个有同样功能的接口叫ApplicationRunner。两者的区别在于所接受的参数类型不一样。

CommandLineRunner的run方法定义如下:

/**
	 * Callback used to run the bean.
	 * @param args incoming main method arguments
	 * @throws Exception on error
	 */
	void run(String... args) throws Exception;

而ApplicationRunner的定义如下:

/**
	 * Callback used to run the bean.
	 * @param args incoming application arguments
	 * @throws Exception on error
	 */
	void run(ApplicationArguments args) throws Exception;

其他方面没什么区别。都是在容器启动时做指定动作。

简单使用示例:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application implements CommandLineRunner {

	@Autowired
	CollectorExecRateService collectorExecRateService;

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

	@Override
	public void run(String... strings) throws Exception {
		DefaultExports.initialize();
		//do something you need
	}
}

最后,对于CommandLineRunner中run方法的执行时机,是在Spring容器完全启动完成的时候被执行。

看下源码中的说明:( 源码位置:SpringApplication.run(String... args),springboot版本:2.0.2 )

try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
            //这里是去调用CommandLineRunner or ApplicationRunner的run方法
			callRunners(context, applicationArguments);
		}

callRunners的方法很简单,就是排序后执行run方法:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
        //注册所有实现两个接口的class
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		//根据order排序
        AnnotationAwareOrderComparator.sort(runners);
        //遍历所有注册的方法,执行其内容
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
                //执行ApplicationRunner中的run
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
                //执行CommandLineRunner中的run
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}


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

猜你喜欢

转载自blog.csdn.net/michaelgo/article/details/81629476