抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

SpringBoot启动时设置初始化数据

在开发中如何配置业务属性到SpringBoot的容器中。

实现ApplicationRunner接口

@Component
public class TestInitClass implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("---------ApplicationRunner 启动之前加载------------");
    }
}

实现CommandLineRunner接口

@Component
public class TestInitClass implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("---------CommandLineRunner 启动之前加载------------");
    }
}

优先级

默认情况下,ApplicationRunnerCommandLineRunner先执行。

源码分析

org.springframework.boot.SpringApplication#run(java.lang.String...)

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    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);
        // 这个方法
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

进入callRunners方法

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    List<Object> runners = new ArrayList<>();
    // 先添加了 ApplicationRunner 接口再添加 CommandLineRunner 接口
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    AnnotationAwareOrderComparator.sort(runners);
    // 循环调用时也是先判断 ApplicationRunner
    for (Object runner : new LinkedHashSet<>(runners)) {
        if (runner instanceof ApplicationRunner) {
            callRunner((ApplicationRunner) runner, args);
        }
        if (runner instanceof CommandLineRunner) {
            callRunner((CommandLineRunner) runner, args);
        }
    }
}

而对于Runner的顺序,关注下AnnotationAwareOrderComparator.sort(runners);这个方法,可以发现只是走list的sort方法排序。

public static void sort(List<?> list) {
    if (list.size() > 1) {
        list.sort(INSTANCE);
    }
}

要想修改执行顺序的话,可以使用order注解,值越小,优先级越高。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {

    /**
     * The order value.
     * <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
     * @see Ordered#getOrder()
     */
    int value() default Ordered.LOWEST_PRECEDENCE;

}

使用该注解指定数值后,结果如下:

总结

  • 所有CommandLineRunner/ApplicationRunner的执行时点是在SpringBoot应用的ApplicationContext完全初始化开始工作之后,从callRunners()可以看出是run方法内部最后一个调用的方法。
  • 只要存在于当前SpringBoot应用的ApplicationContext中的任何CommandLineRunner/ApplicationRunner,都会被加载执行(不管你是手动注册还是自动扫描去Ioc容器)。
  • 使用@Order注解可以修改加载顺序。
  • 一般只需要使用一个接口来加载初始化数据即可。

评论