springboot2啟動(dòng)時(shí)執(zhí)行,初始化(或定時(shí)任務(wù))servletContext問(wèn)題
springboot2啟動(dòng)時(shí)執(zhí)行,初始化(或定時(shí)任務(wù))servletContext
需求:springboot 啟動(dòng)后自動(dòng)執(zhí)行,初始化數(shù)據(jù),并將數(shù)據(jù)放到 servletContext 中。
首先,不可使用 ServletContextListener 即不能用 @WebListener ,因?yàn)?servlet 容器初始化后,spring 并未初始化完畢,不能使用 @Autowired 注入 spring 的對(duì)象。
代碼如下:
可以實(shí)現(xiàn) ApplicationListener
@Component
public class SettingDataInitListener implements ApplicationListener<ContextRefreshedEvent> {
? ? @Override
? ? public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
? ? ? ? WebApplicationContext webApplicationContext =?
? ? ? ? ? ? (WebApplicationContext)contextRefreshedEvent.getApplicationContext();
? ? ? ? ServletContext servletContext = webApplicationContext.getServletContext();
? ? ? ? servletContext.setAttribute("key", "value");
? ? }
}使用注解注入
service類里注入 servletContext
@Autowired private ServletContext servletContext;
service類里要啟動(dòng)執(zhí)行的方法加上注解
@PostConstruct
在定時(shí)任務(wù)里,也可以注入 servletContext,進(jìn)行定時(shí)操作
@Component
public class MyTask {
? ? @Autowired
? ? private ServletContext servletContext;
? ? // 秒 分 時(shí) 日 月 周
? ? @Scheduled(cron = "0 * * * * *")
? ? public void resetDays() {
? ? ? ? // servletContext
? ? }
}springboot啟動(dòng)時(shí)初始化數(shù)據(jù)的幾種方式
在我們用springboot搭建項(xiàng)目的時(shí)候,經(jīng)常碰到在項(xiàng)目啟動(dòng)時(shí)初始化一些字典數(shù)據(jù)、地市數(shù)據(jù)、等各類需求,針對(duì)這種需求Spring與Spring boot為我們提供了以下幾種方案供我們選擇:
- springboot提供的ApplicationRunner與CommandLineRunner接口
- Spring Bean初始化的init-method、PostConstruct注解、
InitializingBean、BeanPostProcessor接口
Spring的事件機(jī)制: 實(shí)現(xiàn) ApplicationListener 接口
一、ApplicationRunner與CommandLineRunner
如果需要在SpringApplication啟動(dòng)時(shí)執(zhí)行一些特殊的代碼,可以通過(guò)實(shí)現(xiàn)ApplicationRunner或CommandLineRunner接口,這兩個(gè)接口都提供單一的run方法,且run方法僅在SpringApplication.run(…)完成之前調(diào)用。
區(qū)別:參數(shù)不一樣,CommandLineRunner的參數(shù)是最原始的參數(shù),沒(méi)有進(jìn)行任何處理,ApplicationRunner的參數(shù)是ApplicationArguments
ApplicationRunner接口只需要自己創(chuàng)建類實(shí)現(xiàn)ApplicationRunner接口
/**
?* @author 重慶阿湯哥
?* @Description: 測(cè)試
?* @date 2021/11/26 ?
?*/
@Component
@Slf4j
public class ApplicationRunnerTest implements ApplicationRunner {
? ? @Override
? ? public void run(ApplicationArguments args) throws Exception {
? ? ? ? log.info("ApplicationRunner init data.....");
? ? }
}CommandLineRunner
對(duì)于這個(gè)接口而言,我們可以通過(guò)Order注解或者使用Ordered接口來(lái)指定調(diào)用順序,@Order()中的值越小,優(yōu)先級(jí)越高
/**
?* @author 重慶阿湯哥
?* @Description: 測(cè)試
?* @date 2021/11/26 ?
?*/
@Component
@Slf4j
@Order(1)
public class CommandLineRunnerTest implements CommandLineRunner {
? ? @Override
? ? public void run(String... args) throws Exception {
? ? ? ? log.info("CommandLineRunner init data.....");
? ? }
}二、Spring Bean初始化的InitializingBean,init-method和PostConstruct
InitializingBean接口、BeanPostProcessor接口
InitializingBean接口為bean提供了初始化方法的方式,它只包括afterPropertiesSet()方法。
在spring初始化bean的時(shí)候,如果bean實(shí)現(xiàn)了InitializingBean接口,在對(duì)象的所有屬性被初始化后之后才會(huì)調(diào)用afterPropertiesSet()方法
/**
?* @author 重慶阿湯哥
?* @Description: 測(cè)試
?* @date 2021/11/26 ?
?*/
@Component
@Slf4j
@Component
public class InitialingzingBeanTest implements InitializingBean {
? ? @Override
? ? public void afterPropertiesSet() throws Exception {
? ? ? ?log.info("InitializingBean init....");
? ? }
}@PostConstruct
/**
?* @author 重慶阿湯哥
?* @Description: 測(cè)試
?* @date 2021/11/26 ?
?*/
@Component
@Slf4j
public class DynamicRouteMonitor {
? ?
? ? @PostConstruct
? ? public void init() {
? ? ? ? log.info("gateway route init...");
? ? ? ? }
}BeanPostProcessor接口
可以用于判斷某些特定類加載完成后才能初始化數(shù)據(jù)的場(chǎng)景,只需要自己實(shí)現(xiàn)該接口中的方法進(jìn)行前置條件判斷
public interface BeanPostProcessor {
? ? @Nullable
? ? default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
? ? ? ? return bean;
? ? }
? ? @Nullable
? ? default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
? ? ? ? return bean;
? ? }
}三、Spring的事件機(jī)制
在Spring中,默認(rèn)對(duì)ApplicationEvent事件提供了如下支持:
ContextStartedEvent:ApplicationContext啟動(dòng)后觸發(fā)的事件ContextStoppedEvent:ApplicationContext停止后觸發(fā)的事件ContextRefreshedEvent:ApplicationContext初始化或刷新完成后觸發(fā)的事件;也就是容器初始化完成后調(diào)用。ContextClosedEvent:ApplicationContext關(guān)閉后觸發(fā)的事件;如web容器關(guān)閉時(shí)自動(dòng)會(huì)觸發(fā)spring容器的關(guān)閉。甚至大家聽(tīng)說(shuō)過(guò)的鉤子程序都是調(diào)用ctx.registerShutdownHook()進(jìn)行注冊(cè)虛擬機(jī)關(guān)閉。
利用ContextRefreshedEvent事件進(jìn)行初始化操作
/**
?* @author 重慶阿湯哥
?* @Description:容器初始化完整后初始字典數(shù)據(jù)
?* @date 2021/11/26 ?10:51
?*/
@Component
public class ApplicationStartup implements ApplicationListener<ContextRefreshedEvent> {
? ? public void onApplicationEvent(ContextRefreshedEvent event) {
? ? ? ? //在容器加載完畢后獲取dao層來(lái)操作數(shù)據(jù)庫(kù)
? ? ? ? ISysDictTypeService sysDictTypeService = (ISysDictTypeService) event.getApplicationContext().getBean(ISysDictTypeService.class);
? ? ? ? sysDictTypeService.initDict();
? ? ? ? IProvCityService provCityService = (IProvCityService) event.getApplicationContext().getBean(IProvCityService.class);
? ? ? ? provCityService.initProvCity();
? ? }
? ? @Override
? ? protected Object clone() throws CloneNotSupportedException {
? ? ? ? return super.clone();
? ? }
}這幾種方式都可以滿足我們?nèi)粘i_(kāi)發(fā)的需求,針對(duì)具體場(chǎng)景使用對(duì)應(yīng)的方案,在微服務(wù)應(yīng)用中使用也較廣泛。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot swagger不顯示接口的問(wèn)題及解決
這篇文章主要介紹了springboot swagger不顯示接口的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
java中的final關(guān)鍵字詳解及實(shí)例
這篇文章主要介紹了 java中的final關(guān)鍵字詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03
Java定時(shí)任務(wù):利用java Timer類實(shí)現(xiàn)定時(shí)執(zhí)行任務(wù)的功能
本篇文章主要介紹了利用java Timer類實(shí)現(xiàn)定時(shí)執(zhí)行任務(wù)的功能,具有一定的參考價(jià)值,有需要的可以了解一下。2016-11-11
Spring依賴注入的兩種方式(根據(jù)實(shí)例詳解)
這篇文章主要介紹了Spring依賴注入的兩種方式(根據(jù)實(shí)例詳解),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05
Java中sleep()與wait()的區(qū)別總結(jié)
因?yàn)樽罱鼘W(xué)習(xí)時(shí)正好碰到這兩個(gè)方法,就查閱相關(guān)資料,并通過(guò)程序?qū)崿F(xiàn),進(jìn)行區(qū)別總結(jié)一下,所以下面這篇文章主要給大家總結(jié)介紹了關(guān)于Java中sleep()與wait()區(qū)別的相關(guān)資料,需要的朋友可以參考,下面來(lái)一起看看吧。2017-05-05

