本篇文章為大家展示了如何在Spring Boot中支持Crontab任務(wù)改造,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。
創(chuàng)新互聯(lián)主要從事網(wǎng)頁(yè)設(shè)計(jì)、PC網(wǎng)站建設(shè)(電腦版網(wǎng)站建設(shè))、wap網(wǎng)站建設(shè)(手機(jī)版網(wǎng)站建設(shè))、成都響應(yīng)式網(wǎng)站建設(shè)、程序開(kāi)發(fā)、網(wǎng)站優(yōu)化、微網(wǎng)站、小程序制作等,憑借多年來(lái)在互聯(lián)網(wǎng)的打拼,我們?cè)诨ヂ?lián)網(wǎng)網(wǎng)站建設(shè)行業(yè)積累了豐富的網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、網(wǎng)絡(luò)營(yíng)銷經(jīng)驗(yàn),集策劃、開(kāi)發(fā)、設(shè)計(jì)、營(yíng)銷、管理等多方位專業(yè)化運(yùn)作于一體。
1. 監(jiān)聽(tīng)目標(biāo)對(duì)象
借助容器刷新事件來(lái)監(jiān)聽(tīng)目標(biāo)對(duì)象即可,可以認(rèn)為,定時(shí)任務(wù)其實(shí)每次只是執(zhí)行一種操作而已。
比如這是一個(gè)寫好的例子,注意不要直接用 @Service 將其放入容器中,除非容器本身沒(méi)有其它自動(dòng)運(yùn)行的事件。
package com.github.zhgxun.learn.common.task; import com.github.zhgxun.learn.common.task.annotation.ScheduleTask; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; /** * 不自動(dòng)加入容器, 用于區(qū)分是否屬于任務(wù)啟動(dòng), 否則放入容器中, Spring 無(wú)法選擇性執(zhí)行 * 需要根據(jù)特殊參數(shù)在啟動(dòng)時(shí)注入 * 該監(jiān)聽(tīng)器本身不能訪問(wèn)容器變量, 如果需要訪問(wèn), 需要從上下文中獲取對(duì)象實(shí)例后方可繼續(xù)訪問(wèn)實(shí)例信息 * 如果其它類中啟動(dòng)了多線程, 是無(wú)法接管異常拋出的, 需要子線程中正確處理退出操作 * 該監(jiān)聽(tīng)器最好不用直接做線程操作, 子類的實(shí)現(xiàn)不干預(yù) */ @Slf4j public class TaskApplicationListener implements ApplicationListener<ContextRefreshedEvent> { /** * 任務(wù)啟動(dòng)監(jiān)聽(tīng)類標(biāo)識(shí), 啟動(dòng)時(shí)注入 * 即是 java -Dspring.task.class=com.github.zhgxun.learn.task.TestTask -jar learn.jar */ private static final String SPRING_TASK_CLASS = "spring.task.class"; /** * 支持該注解的方法個(gè)數(shù), 目前僅一個(gè) * 可以理解為控制臺(tái)一次執(zhí)行一個(gè)類, 依賴的任務(wù)應(yīng)該通過(guò)其它方式控制依賴 */ private static final int SUPPORT_METHOD_COUNT = 1; /** * 保存當(dāng)前容器運(yùn)行上下文 */ private ApplicationContext context; /** * 監(jiān)聽(tīng)容器刷新事件 * * @param event 容器刷新事件 */ @Override @SuppressWarnings("unchecked") public void onApplicationEvent(ContextRefreshedEvent event) { context = event.getApplicationContext(); // 不存在時(shí)可能為正常的容器啟動(dòng)運(yùn)行, 無(wú)需關(guān)心 String taskClass = System.getProperty(SPRING_TASK_CLASS); log.info("ScheduleTask spring task Class: {}", taskClass); if (taskClass != null) { try { // 獲取類字節(jié)碼文件 Class clazz = findClass(taskClass); // 嘗試從內(nèi)容上下文中獲取已加載的目標(biāo)類對(duì)象實(shí)例, 這個(gè)類實(shí)例是已經(jīng)加載到容器內(nèi)的對(duì)象實(shí)例, 即可以獲取類的信息 Object object = context.getBean(clazz); Method method = findMethod(object); log.info("start to run task Class: {}, Method: {}", taskClass, method.getName()); invoke(method, object); } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } finally { // 需要確保容器正常出發(fā)停止事件, 否則容器會(huì)僵尸卡死 shutdown(); } } } /** * 根據(jù)class路徑名稱查找類文件 * * @param clazz 類名稱 * @return 類對(duì)象 * @throws ClassNotFoundException ClassNotFoundException */ private Class findClass(String clazz) throws ClassNotFoundException { return Class.forName(clazz); } /** * 獲取目標(biāo)對(duì)象中符合條件的方法 * * @param object 目標(biāo)對(duì)象實(shí)例 * @return 符合條件的方法 */ private Method findMethod(Object object) { Method[] methods = object.getClass().getDeclaredMethods(); List<Method> schedules = Stream.of(methods) .filter(method -> method.isAnnotationPresent(ScheduleTask.class)) .collect(Collectors.toList()); if (schedules.size() != SUPPORT_METHOD_COUNT) { throw new IllegalStateException("only one method should be annotated with @ScheduleTask, but found " + schedules.size()); } return schedules.get(0); } /** * 執(zhí)行目標(biāo)對(duì)象方法 * * @param method 目標(biāo)方法 * @param object 目標(biāo)對(duì)象實(shí)例 * @throws IllegalAccessException IllegalAccessException * @throws InvocationTargetException InvocationTargetException */ private void invoke(Method method, Object object) throws IllegalAccessException, InvocationTargetException { method.invoke(object); } /** * 執(zhí)行完畢退出運(yùn)行容器, 并將返回值交給執(zhí)行環(huán)節(jié), 比如控制臺(tái)等 */ private void shutdown() { log.info("shutdown ..."); System.exit(SpringApplication.exit(context)); } }
其實(shí)該處僅需要啟動(dòng)執(zhí)行即可,容器啟動(dòng)完畢事件也是可以的。
2. 標(biāo)識(shí)目標(biāo)方法
目標(biāo)方法的標(biāo)識(shí),最方便的是使用注解標(biāo)注。
package com.github.zhgxun.learn.common.task.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface ScheduleTask { }
3. 編寫任務(wù)
package com.github.zhgxun.learn.task; import com.github.zhgxun.learn.common.task.annotation.ScheduleTask; import com.github.zhgxun.learn.service.first.LaunchInfoService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Service @Slf4j public class TestTask { @Autowired private LaunchInfoService launchInfoService; @ScheduleTask public void test() { log.info("Start task ..."); log.info("LaunchInfoList: {}", launchInfoService.findAll()); log.info("模擬啟動(dòng)線程操作"); for (int i = 0; i < 5; i++) { new MyTask(i).start(); } try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } } } class MyTask extends Thread { private int i; private int j; private String s; public MyTask(int i) { this.i = i; } @Override public void run() { super.run(); System.out.println("第 " + i + " 個(gè)線程啟動(dòng)..." + Thread.currentThread().getName()); if (i == 2) { throw new RuntimeException("模擬運(yùn)行時(shí)異常"); } if (i == 3) { // 除數(shù)不為0 int a = i / j; } // 未對(duì)字符串對(duì)象賦值, 獲取長(zhǎng)度報(bào)空指針錯(cuò)誤 if (i == 4) { System.out.println(s.length()); } } }
4. 啟動(dòng)改造
啟動(dòng)時(shí)需要做一些調(diào)整,即跟普通的啟動(dòng)區(qū)分開(kāi)。這也是為什么不要把監(jiān)聽(tīng)目標(biāo)對(duì)象直接放入容器中的原因,在這里顯示添加到容器中,這樣就不影響項(xiàng)目中類似 CommandLineRunner 的功能,畢竟這種功能是容器啟動(dòng)完畢就能運(yùn)行的。如果要改造,會(huì)涉及到很多硬編碼。
package com.github.zhgxun.learn; import com.github.zhgxun.learn.common.task.TaskApplicationListener; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @SpringBootApplication public class LearnApplication { public static void main(String[] args) { SpringApplicationBuilder builder = new SpringApplicationBuilder(LearnApplication.class); // 根據(jù)啟動(dòng)注入?yún)?shù)判斷是否為任務(wù)動(dòng)作即可, 否則不干預(yù)啟動(dòng) if (System.getProperty("spring.task.class") != null) { builder.listeners(new TaskApplicationListener()).run(args); } else { builder.run(args); } } }
5. 啟動(dòng)注入
-Dspring.task.class 即是啟動(dòng)注入標(biāo)識(shí),當(dāng)然這個(gè)標(biāo)識(shí)不要跟默認(rèn)的參數(shù)混淆,需要區(qū)分開(kāi),否則可能始終獲取到系統(tǒng)參數(shù),而無(wú)法獲取用戶參數(shù)。
java -Dspring.task.class=com.github.zhgxun.learn.task.TestTask -jar target/learn.jar
springboot一種全新的編程規(guī)范,其設(shè)計(jì)目的是用來(lái)簡(jiǎn)化新Spring應(yīng)用的初始搭建以及開(kāi)發(fā)過(guò)程,SpringBoot也是一個(gè)服務(wù)于框架的框架,服務(wù)范圍是簡(jiǎn)化配置文件。
上述內(nèi)容就是如何在Spring Boot中支持Crontab任務(wù)改造,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
網(wǎng)站欄目:如何在SpringBoot中支持Crontab任務(wù)改造
本文來(lái)源:http://m.newbst.com/article18/gsejgp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)、搜索引擎優(yōu)化、定制開(kāi)發(fā)、網(wǎng)站導(dǎo)航、外貿(mào)網(wǎng)站建設(shè)、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)