如何在Spring Boot 中動態設定與執行定時任務


 本篇文章的目的是記錄並實現在Spring Boot中,動態設定與執行定時任務。

 我的開發項目是 Maven 項目,所以首先需要在 pom.xml 文件中加入相關的依賴。依賴代碼如下所示:

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>RELEASE</version>
</dependency>

 

下圖是定時任務的列表及功能展示。

一般情況下,定時任務列表都是初始化生成,所以此處並無定時任務的新增功能。每個定時任務的操作欄中都有三種操作,分別是  頻率設置、啟用(禁用)、手動執行。

頻率設置:設置定時任務的執行頻率,設置成功后,可以重新設置定時任務的執行頻率,這個功能是動態設定執行頻率的保證。

啟用(禁用):控制定時任務的執行狀態,定時執行或者不執行。啟用前,需先設置定時任務的執行頻率。

手動執行:手動調用定時任務的執行方法,無需等到下次執行時間。手動執行前,定時任務的狀態應該為啟用狀態。

 

 

在執行頻率的設定上,我選擇了直接使用Cron表達式,為了簡單與方便,我在末端小圖標上加了一個鏈接,點擊圖標后,便會跳轉到在線Cron表達式頁面。

<div class="row">
    <label class="label col col-3 text-align-right">頻率設置<font color="red">*</font>:</label>
    <div class="col col-8">
         <div class="input-group ">
             <input type="text" class="form-control" id="cron" name="cron" th:value="${monitor.cron}" placeholder="點擊末端小圖標在線獲取Cron表達式"/>
             <span onclick="getCron()" class="input-group-addon"><i class="fa fa-calendar"></i></span>
         </div>
    </div>
</div>
//在線獲取Cron表達式
function getCron() {
     window.open("http://cron.qqe2.com/");  //打開一個新的網頁,不會覆蓋當前網頁
 }

 

在新打開的網頁上,根據需求設定執行頻率,將生成的Cron表達式復制到文本框中,點擊 “確定” 按鈕。下面的代碼是點擊 “確定” 按鈕后,后台的處理邏輯。

  @RequestMapping(value = "setFrequencyCron", method = RequestMethod.POST)
  @ResponseBody
  @OperationLog(success = "設置頻率成功", failure = "設置頻率失敗")
  public Json setFrequencyCron(Monitor monitor) {
        try {
            Monitor m=monitorService.getMonitorById(monitor.getId());
            m.setCron(monitor.getCron());
            CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator(monitor.getCron());
            Date currentTime = new Date();  //當前系統時間
            Date nextTimePoint = cronSequenceGenerator.next(currentTime);   //下次執行時間
            Date nextNextTimePoint = cronSequenceGenerator.next(nextTimePoint);  //下下次執行時間
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String nextTime=sdf.format(nextTimePoint);
            String nextDoubleTme=sdf.format(nextNextTimePoint);
            m.setNextTime(nextTime);
            m.setNextDoubleTime(nextDoubleTme);
            m.setEnable("0");   //設置頻率,讓其狀態變為“禁用”
            monitorService.updateMonitor(m);
            return Json.ok("頻率設置成功");
        } catch (Exception e) {
            LOGGER.error("頻率設置失敗: " + e.getMessage(), e);
            return Json.fail("頻率設置失敗");
        }
    }

 

 

根據Cron表達式,使用 Spring 自帶的 CronSequenceGenerator 類可以獲得下次執行時間和下下次執行時間。每次設定新的執行頻率后,該定時任務的狀態都會變為“禁用”,需要重新啟用,定時任務才能生效。

下面的代碼是點擊 “啟用”或“禁用” 按鈕后,后台的處理邏輯。

    @RequestMapping(value = "setEnable", method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    @OperationLog(success = "操作成功", failure = "操作失敗")
    public Json setEnable(Long id, String enable) {
        Json json=new Json();
        try {
            String msg="";
            Monitor monitor=monitorService.getMonitorById(id);
            monitor.setEnable(enable);
            monitorService.updateMonitor(monitor);
            if (enable.equals("1")){
                msg="啟用成功";
            }else if (enable.equals("0")){
                msg="禁用成功";
            }

            //啟用或禁用時,清空redis中的監控信息
            redisUtils.remove(KEY);

            json.setMsg(msg);
            json.setSuccess(true);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            return Json.fail("操作失敗");
        }
        return json;
    }

 

在定時任務的執行過程中,為了減少與數據庫的交互,所以使用了緩存工具類  RedisUtils ,會將最新的定時任務信息保存到 RedisUtils 中。所以在啟用與禁用定時任務時,將會清除以前保存在 RedisUtils 中的定時任務信息,加載最新的定時任務信息。

 

在啟動類的上方加上  @EnableScheduling 注解,該注解的作用是開啟Spring Boot對定時任務的支持。

@EnableScheduling
public class DueUIApplication extends AbstractStdApplication {public static void main(String[] args) {
        SpringApplication.run(DueUIApplication.class, args);
    }

}

 

新建一個定時任務執行類 MonitorTask  ,在該類中加一個用於掃描定時任務的方法,在該方法上方需要加上 @Scheduled 注解,同時需要帶上參數,設定掃描頻率。

代碼如下所示:

@Component
public class MonitorTask extends AbstractStdAction {
    private static final Logger LOGGER = Logger.getLogger(MonitorTask.class);

    @Autowired
    private MonitorService monitorService;

    private String KEY="pbeechina:due:ui:monitor:action";    //redis的key


    /**
     * 每隔一分鍾秒掃描一次監控信息
     */
    @Scheduled(cron = "0 0/1 * * * ? ")
    @Transactional(propagation= Propagation.NOT_SUPPORTED)   //不需要事物管理
    public void scanMonitor(){
        try {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("**********開始掃描監控的定時任務***********");
            }

            List<Monitor> monitorList=null;  //預警監控信息

            boolean isKey=redisUtils.exists(KEY);   //判斷key值是否存在
            if (isKey){   //key存在,代表監控信息是最新的
                monitorList=(List<Monitor>)redisUtils.get(KEY);
            }else {
                monitorList=monitorService.getMonitorList();   //查詢所有已被啟用的監控
                if (monitorList != null && monitorList.size() > 0){
                    redisUtils.set(KEY,monitorList, 1,TimeUnit.DAYS);  //設置過期時間一天
                }
            }

            if (monitorList != null && monitorList.size() > 0){
                for (Monitor monitor:monitorList){
                    if(StringUtils.isNotEmpty(monitor.getCron())){
                        CronExpression expression = new CronExpression(monitor.getCron());
                        if(expression.isSatisfiedBy(new Date(System.currentTimeMillis()))){
                            LOGGER.info("開始執行定時任務...");
                            monitorService.autoExecute(monitor);
                        }
                    }
                }
            }

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("*******掃描結束***********");
            }
        } catch (Exception e) {
            LOGGER.error("監控定時任務掃描失敗", e);
        }
    }
}

 

至此,Spring Boot動態執行定時任務的功能就實現了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM