我们在使用SpringBoot搭建项目的时候,如果希望在项目启动完成之前,能够初始化一些操作,针对这种需求,可以考虑实现如下两个接口(任一个都可以)
-
org.springframework.boot.CommandLineRunner
-
org.springframework.boot.ApplicationRunner
1.CommandLineRunner接口的使用
-
@Component public class TestCommandLineRunner implements CommandLineRunner { @Override // 实现该接口之后,实现其run方法,可以在run方法中自定义实现任务 public void run(String... args) throws Exception { System.out.println("服务启动,TestCommandLineRunner执行启动加载任务..."); if(null != args){ for (String s : args) { System.out.println(s); } } } } @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication application = new SpringApplication(Application.class); ConfigurableApplicationContext context = application.run(args); context.close(); } }
启动Application类,可以看到TestCommandLineRunner.run()方法被执行。
至于String... args参数,是用户在启动Application的时候,通过Run Configurations中的Arguments添加的参数(参数格式为KV格式,类似于--foo=bar),如下图所示
2.ApplicationRunner接口的使用
-
@Component public class TestApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { System.out.println("服务启动,TestApplicationRunner执行启动加载任务..."); String[] sourceArgs = args.getSourceArgs(); if(null != sourceArgs){ for (String s : sourceArgs) { System.out.println(s); } } } } @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication application = new SpringApplication(Application.class); ConfigurableApplicationContext context = application.run(args); context.close(); } }
ApplicationRunner接口的使用方式与CommandLineRunner接口基本相似,不同的只是run方法的参数类型,CommandLineRunner是基本类型,而ApplicationRunner是ApplicationArguments对象类型,经过一次简单封装,用户可对参数进行更多操作,具体可见ApplicationArguments的方法
总结:用户使用CommandLineRunner或者ApplicationRunner接口均可实现应用启动初始化某些功能的需求,如果希望对参数有更多的操作,则可以选择实现ApplicationRunner接口。
3.CommandLineRunner、ApplicationRunner执行流程源码分析
用户只要实现这两个接口,其中的run方法就会在项目启动时候被自动调用,那么究竟是在什么时候调用的呢?下面可以随笔者看一下Application的启动流程
1)SpringApplication.run(args)
-
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.started(); try { // 对用户在Arguments输入的参数进行封装 ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); Banner printedBanner = printBanner(environment); context = createApplicationContext(); analyzers = new FailureAnalyzers(context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); // 在更新完ApplicationContext之后,进行操作,run方法的调用就在这里被执行 afterRefresh(context, applicationArguments); listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); } }
2)跟踪afterRefresh(ConfigurableApplicationContext context,ApplicationArguments args)方法,代码如下:
-
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { callRunners(context, args); } private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<Object>(); // 1.获取所有实现ApplicationRunner接口的类 runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); // 2.获取所有实现CommandLineRunner的类 runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); // 3.根据其@Order进行排序 AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<Object>(runners)) { // 4.调用ApplicationRunner其run方法 if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } // 5.调用CommandLineRunner其run方法 if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }
3)跟踪context.getBeansOfType()方法,具体实现如下(在类DefaultListableBeanFactory中):
-
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { callRunners(context, args); } private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<Object>(); // 1.获取所有实现ApplicationRunner接口的类 runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); // 2.获取所有实现CommandLineRunner的类 runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); // 3.根据其@Order进行排序 AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<Object>(runners)) { // 4.调用ApplicationRunner其run方法 if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } // 5.调用CommandLineRunner其run方法 if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }
总结:通过以上分析可知,实现这两个接口的类,在ApplicationContext.run()方法里被执行