我们在开发过程中会有这样的场景:需要在容器启动的时候执行一些内容,比如:读取配置文件信息,数据库连接,删除临时文件,清除缓存信息,在Spring框架下是通过ApplicationListener监听器来实现的。在Spring Boot中给我们提供了两个接口来帮助我们实现这样的需求。这两个接口就是我们今天要讲的CommandLineRunner和ApplicationRunner,他们的执行时机为容器启动完成的时候。使用起来非常简单。只需要实现CommandLineRunner或者ApplicationRunner接口

for example:

package com.batch.demo.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

/**
 * Created by WuTing on 2018/2/13.
 */
@Component
public class CommandExecute implements CommandLineRunner {
    private Logger LOG = LoggerFactory.getLogger(CommandExecute.class);

    @Autowired
    private ApplicationContext context;

    @Override
    public void run(String... strings) throws Exception {
        LOG.debug("CommandExecute run");
    }
}

当项目启动成功后,就会执行run方法。那如果存在多个Runner又是如何执行的呢?
我们可以通过@Order或者实现Ordered来设置执行顺序。执行顺序是从小到大。

那到底runner是如何会在项目启动后执行的呢?我们可以通过程序入口跟踪代码找到。
1、找到程序入口,SpringApplication.run()
2、跟踪run方法直至找到SpringApplication的public ConfigurableApplicationContext run(String… args)方法,在该方法中我们可以看到调用了afterRefresh方法,afterRefresh方法就是对runner的调用。

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            Banner printedBanner = this.printBanner(environment);
            context = this.createApplicationContext();
            new FailureAnalyzers(context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments); //调用runner
            listeners.finished(context, (Throwable)null);
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
            throw new IllegalStateException(var9);
        }
    }
 protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
        this.callRunners(context, 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);  //调用runner
            }

            if (runner instanceof CommandLineRunner) {
                this.callRunner((CommandLineRunner)runner, args);  //调用runner
            }
        }

    }

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

从源码可以看出, 会默认调用ApplicationRunner和CommandLineRunner类型的run方法。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐