SpringBoot中提供了两个接口可以在Spring Boot启动的过程中进行一些额外的操作,比如读取配置文件、数据库操作等自定义的内容。 而这些功能的实现也非常简单,直接实现这两个接口并实现其run方法,然后将该类实例化即可。以下代码便实现了CommandLineRunner接口,并在run方法内打印了对应的日志,同时,通过@Component将其注册为Spring的一个bean。
@Component
@Slf4j
public class Runner implements ApplicationRunner {
@Override
public void run ( ApplicationArguments args) {
log. info ( "Runner执行成功" ) ;
}
}
CommandLineRunner和ApplicationRunner的源代码:
public interface CommandLineRunner {
void run ( String . . . args) throws Exception ;
}
public interface ApplicationRunner {
void run ( ApplicationArguments args) throws Exception ;
}
通过源代码的对比会发现,它们唯一不同便是run方法的参数。在实际应用中它们的区别也只有这些。
通过接口的官方文档,我们得知其实执行CommandLineRunner和ApplicationRunner的实现类是有顺序的,只不过在示例中并没有展示。针对上面的示例,我们可以通过@Order或实现Ordered接口来对其指定执行顺序。
@Order ( value = 1 )
@Component
@Slf4j
public class Runner implements ApplicationRunner {
@Override
public void run ( ApplicationArguments args) {
log. info ( "Runner执行成功" ) ;
}
}
说到执行顺序,那么再进一步了解一下这两个方法是在什么时候执行的。这两个接口的实现执行的时机在于SpringApplication初始化之后,调用的run方法中被调用的。
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;
}
我们可以看到,在try方法的最后,会执行一个callRunners的方法,在此方法中会对实现这两个接口的实现类进行调用。
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) ;
for ( Object runner : new LinkedHashSet < > ( runners) ) {
if ( runner instanceof ApplicationRunner ) {
callRunner ( ( ApplicationRunner ) runner, args) ;
}
if ( runner instanceof CommandLineRunner ) {
callRunner ( ( CommandLineRunner ) runner, args) ;
}
}
}
private void callRunner ( ApplicationRunner runner, ApplicationArguments args) {
try {
( runner) . run ( args) ;
}
catch ( Exception ex) {
throw new IllegalStateException ( "Failed to execute ApplicationRunner" , ex) ;
}
}
private void callRunner ( CommandLineRunner runner, ApplicationArguments args) {
try {
( runner) . run ( args. getSourceArgs ( ) ) ;
}
catch ( Exception ex) {
throw new IllegalStateException ( "Failed to execute CommandLineRunner" , ex) ;
}
}
参考链接:SpringBoot中CommandLineRunner和ApplicationRunner接口解析和使用