SpringBoot项目启动时执行特定代码

The SpringBoot project executes specific code at start up

Posted by if on 2022-01-08
Estimated Reading Time 4 Minutes
Words 978 In Total

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

使用方法

其实CommandLineRunnerApplicationRunner使用方法差不多

使用@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启动类中同时实现ApplicationRunnerCommandLineRunner接口,并调用这个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);
}
}

本个人博客提供的内容仅用于个人学习,不保证内容的正确性。通过使用本站内容随之而来的风险与本站无关!