前言:
我们在使用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 runners = new ArrayList();
// 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(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中):
public Map getBeansOfType(Class type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
// 根据类型获取beanName
String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
Map result = new LinkedHashMap(beanNames.length);
for (String beanName : beanNames) {
try {
result.put(beanName, getBean(beanName, type));
}
...
}
return result;
}
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
List result = new ArrayList();
// 检查容器中所有的bean
for (String beanName : this.beanDefinitionNames) {
// Only consider bean as eligible if the bean name
// is not defined as alias for some other bean.
if (!isAlias(beanName)) {
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Only check bean definition if it is complete.
if (!mbd.isAbstract() && (allowEagerInit ||
((mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading())) &&
!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
// In case of FactoryBean, match object created by FactoryBean.
boolean isFactoryBean = isFactoryBean(beanName, mbd);
BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
boolean matchFound =
(allowEagerInit || !isFactoryBean ||
(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
(includeNonSingletons ||
(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
isTypeMatch(beanName, type);
if (!matchFound && isFactoryBean) {
// In case of FactoryBean, try to match FactoryBean instance itself next.
beanName = FACTORY_BEAN_PREFIX + beanName;
matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
}
// 对于符合类型的,添加到result中
if (matchFound) {
result.add(beanName);
}
}
}
...
}
return StringUtils.toStringArray(result);
}
总结:通过以上分析可知,实现这两个接口的类,在ApplicationContext.run()方法里被执行