我的博客原文鏈接
前言
通常的我們的項目開發中,經常會遇到那種在服務一啓動就需要自動執行一些業務代碼的情況。比如將數據庫中的配置信息或者數據字典之類的緩存到redis,或者在服務啓動的時候將一些配置化的定時任務開起來。關於spring mvc或者springboot如何在項目啓動的時候就執行一些代碼,方法其實有很多,我這邊介紹一下我使用過的三種。
1、@PostConstruct
註解
從Java EE5規範開始,Servlet中增加了兩個影響Servlet生命週期的註解,@PostConstruct
和@PreDestroy
,這兩個註解被用來修飾一個非靜態的void()方法。@PostConstruct
會在所在類的構造函數執行之後執行,在init()方法執行之前執行。(@PreDestroy
註解的方法會在這個類的destory()方法執行之後執行。)
- 使用示例:在Spring容器加載之後,我需要啓動定時任務去做任務的處理(我的定時任務採用的是讀取數據庫配置的方式)。在這裏我使用
@PostConstruct
指定了需要啓動的方法。
@Component // 注意 這裏必須有
public class StartAllJobInit {
protected Logger logger = LoggerFactory.getLogger(getClass().getName());
@Autowired
JobInfoService jobInfoService;
@Autowired
JobTaskUtil jobTaskUtil;
@PostConstruct // 構造函數之後執行
public void init(){
System.out.println("容器啓動後執行");
startJob();
}
public void startJob() {
List<JobInfoBO> list = jobInfoService.findList();
for (JobInfoBO jobinfo :list) {
try {
if("0".equals(jobinfo.getStartWithrun())){
logger.info("任務{}未設置自動啓動。", jobinfo.getJobName());
jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STOP);
}else{
logger.info("任務{}設置了自動啓動。", jobinfo.getJobName());
jobTaskUtil.addOrUpdateJob(jobinfo);
jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STARTING);
}
} catch (SchedulerException e) {
logger.error("執行定時任務出錯,任務名稱 {} ", jobinfo.getJobName());
}
}
}
}
2、實現CommandLineRunner
接口並重寫run()方法
CommandLineRunner
接口文檔描述如下:
/**
* Interface used to indicate that a bean should <em>run</em> when it is contained within
* a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined
* within the same application context and can be ordered using the {@link Ordered}
* interface or {@link Order @Order} annotation.
* <p>
* If you need access to {@link ApplicationArguments} instead of the raw String array
* consider using {@link ApplicationRunner}.
*
* @author Dave Syer
* @see ApplicationRunner
*/
public interface CommandLineRunner {
/**
* Callback used to run the bean.
* @param args incoming main method arguments
* @throws Exception on error
*/
void run(String... args) throws Exception;
}
如上所說:接口被用作加入Spring容器中時執行run(String… args)方法,通過命令行傳遞參數。SpringBoot在項目啓動後會遍歷所有實現CommandLineRunner的實體類並執行run方法,多個實現類可以並存並且根據order註解排序順序執行。這邊還有個ApplicationRunner
接口,但是接收參數是使用的ApplicationArguments
。這邊不再贅述。
同樣是啓動時執行定時任務,使用這種方式我的寫法如下:
@Component // 注意 這裏必須有
//@Order(2) 如果有多個類需要啓動後執行 order註解中的值爲啓動的順序
public class StartAllJobInit implements CommandLineRunner {
protected Logger logger = LoggerFactory.getLogger(getClass().getName());
@Autowired
JobInfoService jobInfoService;
@Autowired
JobTaskUtil jobTaskUtil;
@Override
public void run(String... args) {
List<JobInfoBO> list = jobInfoService.findList();
for (JobInfoBO jobinfo :list) {
try {
if("0".equals(jobinfo.getStartWithrun())){
logger.info("任務{}未設置自動啓動。", jobinfo.getJobName());
jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STOP);
}else{
logger.info("任務{}設置了自動啓動。", jobinfo.getJobName());
jobTaskUtil.addOrUpdateJob(jobinfo);
jobInfoService.updateJobStatus(jobinfo.getId(), BasicsConstantManual.BASICS_SYS_JOB_STATUS_STARTING);
}
} catch (SchedulerException e) {
logger.error("執行定時任務出錯,任務名稱 {} ", jobinfo.getJobName());
}
}
}
}
3、使用ContextRefreshedEvent
事件(上下文件刷新事件)
ContextRefreshedEvent 官方在接口上的doc說明
Event raised when an {@code ApplicationContext} gets initialized or refreshed.
ContextRefreshedEvent是Spring的ApplicationContextEvent一個實現,ContextRefreshedEvent 事件會在Spring容器初始化完成後以及刷新時觸發。
在這裏我需要在springboot程序啓動之後加載配置信息和字典信息到Redis緩存中去,我可以這樣寫:
@Component // 注意 這個也是必須有的註解 三種都需要 使spring掃描到這個類並交給它管理
public class InitRedisCache implements ApplicationListener<ContextRefreshedEvent> {
static final Logger logger = LoggerFactory
.getLogger(InitRedisCache.class);
@Autowired
private SysConfigService sysConfigService;
@Autowired
private SysDictService sysDictService;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
logger.info("-------加載配置信息 start-------");
sysConfigService.loadConfigIntoRedis();
logger.info("-------加載配置信息 end-------");
logger.info("-------加載字典信息 start-------");
sysDictService.loadDictIntoRedis();
logger.info("-------加載字典信息 end-------");
}
}
注意:這種方式在springmvc-spring的項目中使用的時候會出現執行兩次的情況。這種是因爲在加載spring和springmvc的時候會創建兩個容器,都會觸發這個事件的執行。這時候只需要在onApplicationEvent
方法中判斷是否有父容器即可。
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if(event.getApplicationContext().getParent() == null){//root application context 沒有parent,他就是老大.
//需要執行的邏輯代碼,當spring容器初始化完成後就會執行該方法。
}
}
總結
以上,就是我在實際開發中常用的三種,在項目啓動時執行代碼的方式,開發者可以根據不同的使用情況選擇合適的方法去執行自己的業務邏輯。