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 {@Overridepublic 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 {@Overridepublic 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> {@Overridepublic 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;}@Overridepublic void starting() {log.info("starting...");}@Overridepublic void environmentPrepared(ConfigurableEnvironment environment) {log.info("environmentPrepared...");}@Overridepublic void contextPrepared(ConfigurableApplicationContext context) {log.info("contextPrepared...");}@Overridepublic void contextLoaded(ConfigurableApplicationContext context) {log.info("contextLoaded...");}@Overridepublic void started(ConfigurableApplicationContext context) {log.info("started...");}@SneakyThrows@Overridepublic 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);}@Overridepublic 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 startlisteners.started(context);// 調用所有的Runnerthis.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);}}