SpringBoot项目启动时执行特定代码
前言
(其实直接在main方法里写也不是执行不了)
如果只是简单的一些语句,写在main中可能会方便一些
但如果需要调用spring容器中的对象可能会要吃瘪,因为main方法是static的,而获取ioc对象不能使用static直接获取(会报错)
当调用@AutoWired
获得ioc容器中的对象时
1 2
| @Autowired private static TestService testService;
|
Exception in thread “main” java.lang.NullPointerException
当调用@Resource
获得ioc容器中的对象时
1 2
| @Resource private static TestService testService;
|
Caused by: java.lang.IllegalStateException: @Resource annotation is not supported on static fields
两个函数接口
有两个函数接口类都可以实现
1.ApplicationRunner接口
源码
1 2 3 4 5 6
| package org.springframework.boot;
@FunctionalInterface public interface ApplicationRunner { void run(ApplicationArguments args) throws Exception; }
|
使用方法
使用@SpringBootApplication
注释的ApplicationMain启动类实现ApplicationRunner
接口,并实现run方法即可
在main方法将spring启动完成后会执行run方法中的程序
1 2 3 4 5 6 7 8 9 10 11 12
| @SpringBootApplication public class BootTestApplication implements ApplicationRunner{
public static void main(String[] args) { SpringApplication.run(BootTestApplication.class, args); }
@Override public void run(ApplicationArguments args) throws Exception { System.out.println("ApplicationRunner.run"); } }
|
2.CommandLineRunner接口
源码
1 2 3 4 5 6
| package org.springframework.boot;
@FunctionalInterface public interface CommandLineRunner { void run(String... args) throws Exception; }
|
使用方法
其实CommandLineRunner
和ApplicationRunner
使用方法差不多
使用@SpringBootApplication
注释的ApplicationMain启动类实现接口,并实现run方法即可
在main方法将spring启动完成后会执行run方法中的程序
1 2 3 4 5 6 7 8 9 10 11 12
| @SpringBootApplication public class BootTestApplication implements CommandLineRunner {
public static void main(String[] args) { SpringApplication.run(BootTestApplication.class, args); }
@Override public void run(String... args) throws Exception { System.out.println("CommandLineRunner.run"); } }
|
为什么会被执行?两个接口谁先执行?为什么?
举个栗子
我们来看个例子
现在我们有一个service类
1 2 3 4 5 6
| @Service public class TestService { public void test(String name){ System.out.println(name); } }
|
然后main启动类中同时实现ApplicationRunner
和CommandLineRunner
接口,并调用这个service的方法
看看是谁先被输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @SpringBootApplication public class BootTestApplication implements ApplicationRunner , CommandLineRunner {
@Autowired private TestService service;
public static void main(String[] args) { SpringApplication.run(BootTestApplication.class, args); }
@Override public void run(ApplicationArguments args) throws Exception { service.test("ApplicationRunner.run"); }
@Override public void run(String... args) throws Exception { service.test("CommandLineRunner.run"); } }
|
结果
可以看到,无论运行几次,结果都是ApplicationRunner
先执行,CommandLineRunner
后执行
ApplicationRunner.run
CommandLineRunner.run
如图

简单源码分析
我们查看SpringApplication.run(BootTestApplication.class, args)
一层一层被调用到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList(); this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting();
Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment); context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); this.refreshContext(context); this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); }
listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); }
try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }
|
其中有一条this.callRunners(context, applicationArguments);
就是回调方法了,我们点开可以看到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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); }
if (runner instanceof CommandLineRunner) { this.callRunner((CommandLineRunner)runner, args); } } }
|
如果启动类是ApplicationRunner
的实现类,那么会调用runner.run(args);
也就是启动类中实现了ApplicationRunner
的run方法
1 2 3 4 5 6 7 8 9 10 11
| if(runner instanceof ApplicationRunner) { this.callRunner((ApplicationRunner)runner, args); }
private void callRunner(ApplicationRunner runner, ApplicationArguments args) { try { runner.run(args); } catch (Exception var4) { throw new IllegalStateException("Failed to execute ApplicationRunner", var4); } }
|
如果启动类是CommandLineRunner
的实现类,那么会调用runner.run(args.getSourceArgs());
也就是启动类中实现了CommandLineRunner
的run方法
1 2 3 4 5 6 7 8 9 10 11
| if (runner instanceof CommandLineRunner) { this.callRunner((CommandLineRunner)runner, args); }
private void callRunner(CommandLineRunner runner, ApplicationArguments args) { try { runner.run(args.getSourceArgs()); } catch (Exception var4) { throw new IllegalStateException("Failed to execute CommandLineRunner", var4); } }
|
本个人博客提供的内容仅用于个人学习,不保证内容的正确性。通过使用本站内容随之而来的风险与本站无关!