SpringBoot系列之启动成功后执行业务逻辑。在Springboot项目中经常会遇到需要在项目启动成功后,加一些业务逻辑的,比如缓存的预处理,配置参数的加载等等场景,下面给出一些常有的方法
实验环境
- JDK 1.8
- SpringBoot 2.2.1
- Maven 3.2+
- Mysql 8.0.26
- 开发工具
-
IntelliJ IDEA
-
smartGit
-
动手实践
- ApplicationRunner和CommandLineRunner
比较常有的使用Springboot框架提供的ApplicationRunner
和CommandLineRunner
,这两种Runner可以实现在Springboot项目启动后,执行我们自定义的业务逻辑,然后执行的顺序可以通过@Order
进行排序,参数值越小,越早执行
写个测试类实现ApplicationRunner
接口,注意加上@Component
才能被Spring容器扫描到
package com.example.jedis.runner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(1)
@Component
@Slf4j
public class TestApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("TestApplicationRunner");
}
}
实现CommandLineRunner
接口
package com.example.jedis.runner;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(2)
@Component
@Slf4j
public class TestCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
log.info("TestCommandLineRunner");
}
}
- ApplicationListener加ApplicationStartedEvent
SpringBoot基于Spring框架的事件监听机制,提供ApplicationStartedEvent
可以对SpringBoot启动成功后的监听,基于事件监听机制,我们可以在SpringBoot启动成功后做一些业务操作
package com.example.jedis.listener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class TestApplicationListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
log.info("onApplicationEvent");
}
}
- SpringApplicationRunListener
如果要在启动的其它阶段做业务操作,可以实现SpringApplicationRunListener
接口,例如要实现打印swagger的api接口文档url,可以在对应方法进行拓展即可
package com.example.jedis.listener;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import java.net.InetAddress;
@Slf4j
public class TestSpringApplicationRunListener implements SpringApplicationRunListener {
private final SpringApplication application;
private final String[] args;
public TestSpringApplicationRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
}
@Override
public void starting() {
log.info("starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
log.info("environmentPrepared...");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
log.info("contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
log.info("contextLoaded...");
}
@Override
public void started(ConfigurableApplicationContext context) {
log.info("started...");
}
@SneakyThrows
@Override
public void running(ConfigurableApplicationContext context) {
log.info("running...");
ConfigurableEnvironment environment = context.getEnvironment();
String port = environment.getProperty("server.port");
String contextPath = environment.getProperty("server.servlet.context-path");
String docPath = port + "" + contextPath + "/doc.html";
String externalAPI = InetAddress.getLocalHost().getHostAddress();
log.info("\n Swagger API: "
+ "Local-API: \t\thttp://127.0.0.1:{}\n\t"
+ "External-API: \thttp://{}:{}\n\t",
docPath, externalAPI, docPath);
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
log.info("failed...");
}
}
在/META-INF/spring.factories配置文件配置:
org.springframework.boot.SpringApplicationRunListener=\
com.example.jedis.listener.TestSpringApplicationRunListener
源码分析
在Springboot的run方法里找到如下的源码,大概看一下就可以知道里面是封装了对Runner
和SpringApplicationRunListener
的调用
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);
// SpringApplicationRunListener调用
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);
}
// SpringApplicationRunListener start
listeners.started(context);
// 调用所有的Runner
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// SpringApplicationRunListener running执行
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}