<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          SpringBoot如何實(shí)現(xiàn)定時(shí)任務(wù),寫得太好了 !

          共 9328字,需瀏覽 19分鐘

           ·

          2021-10-18 08:15

          寫在前面

          SpringBoot創(chuàng)建定時(shí)任務(wù)的方式很簡單,主要有兩種方式:一、基于注解的方式(@Scheduled)二、數(shù)據(jù)庫動(dòng)態(tài)配置。實(shí)際開發(fā)中,第一種需要在代碼中寫死表達(dá)式,如果修改起來,又得重啟會顯示很麻煩;所以我們往往會采取第二種方式,可以直接從數(shù)據(jù)庫中讀取定時(shí)任務(wù)的指定執(zhí)行時(shí)間,無需重啟。

          ?

          下面就來介紹下這兩種方式吧

          一、基于注解(@Scheduled)

          基于注解是一種靜態(tài)的方式,只需要幾行代碼就可以搞定了

          添加一個(gè)配置類

          1?@Configuration??//標(biāo)記配置類
          ?2?@EnableScheduling???//開啟定時(shí)任務(wù)
          ?3?public?class?MyScheduleConfig?{
          ?4?
          ?5?????//添加定時(shí)任務(wù)
          ?6?????@Scheduled(cron?=?"0/5?*?*?*?*??")
          ?7?????private?void?myTasks()?{
          ?8?????????System.out.println("執(zhí)行定時(shí)任務(wù)?"?+?LocalDateTime.now());
          ?9?????}
          10?}

          上面代碼的cron表達(dá)式表示每5秒執(zhí)行一次,可以通過這個(gè)網(wǎng)站(https://qqe2.com/cron)去生成要的cron表達(dá)式

          啟動(dòng)應(yīng)用,控制臺看效果

          ?

          這個(gè)方式的確很簡單方便,但前面介紹也說到了,有個(gè)缺點(diǎn)就是當(dāng)我們需要去修改定時(shí)任務(wù)的執(zhí)行周期或者停止的時(shí)候,我們需要到代碼層去修改,重啟。

          ?

          二、數(shù)據(jù)庫動(dòng)態(tài)配置

          這里使用MySQL數(shù)據(jù)庫

          1、表數(shù)據(jù)添加,資源配置

          1.1 添加表

          CREATE?TABLE?`scheduled_job`?(
          ??`job_id`?int(11)?NOT?NULL?AUTO_INCREMENT?COMMENT?'主鍵id',
          ??`job_key`?varchar(128)?NOT?NULL?COMMENT?'定時(shí)任務(wù)完整類名',
          ??`cron_expression`?varchar(20)?NOT?NULL?COMMENT?'cron表達(dá)式',
          ??`task_explain`?varchar(50)?NOT?NULL?DEFAULT?''?COMMENT?'任務(wù)描述',
          ??`status`?tinyint(4)?NOT?NULL?DEFAULT?'1'?COMMENT?'狀態(tài),1:正常;-1:停用',
          ??PRIMARY?KEY?(`job_id`),
          ??UNIQUE?KEY?`job_key`?(`job_key`),
          ??UNIQUE?KEY?`cron_key_unique_idx`?(`job_key`)
          )?ENGINE=InnoDB?AUTO_INCREMENT=1?DEFAULT?CHARSET=utf8?COLLATE=utf8_bin?COMMENT='定時(shí)任務(wù)表';


          1.2 插入兩條數(shù)據(jù),job_key根據(jù)是完整的類名

          ?

          1.3 引入依賴?


          ????????????org.springframework.boot
          ????????????spring-boot-starter-web
          ????????

          ????????
          ????????
          ????????????mysql
          ????????????mysql-connector-java
          ????????????5.1.49
          ????????????runtime
          ????????

          ????????
          ????????
          ????????????com.baomidou
          ????????????mybatis-plus-boot-starter
          ????????????3.3.1.tmp
          ????????

          ????????
          ????????
          ????????????org.projectlombok
          ????????????lombok
          ????????????1.18.20
          ????????????provided
          ????????

          ?

          1.4 配置application.yml

          spring:
          ??datasource:
          ????url:?jdbc:mysql://127.0.0.1:3306/test?userUnicode=true&characterEncoding=UTF8&useSSL=false
          ????username:?root
          ????password:?123
          ????driver-class-name:?com.mysql.jdbc.Driver
          server:
          ??servlet:
          ????context-path:?/demo
          ??port:?8888

          ?

          2、瘋狂貼代碼

          2.1 創(chuàng)建定時(shí)任務(wù)線程池

          @Configuration
          @Slf4j
          public?class?ScheduledConfig?{

          ????@Bean
          ????public?ThreadPoolTaskScheduler?threadPoolTaskScheduler()?{
          ????????log.info("創(chuàng)建定時(shí)任務(wù)調(diào)度線程池?start");
          ????????ThreadPoolTaskScheduler?threadPoolTaskScheduler?=?new?ThreadPoolTaskScheduler();
          ????????threadPoolTaskScheduler.setPoolSize(20);
          ????????threadPoolTaskScheduler.setThreadNamePrefix("taskExecutor-");
          ????????threadPoolTaskScheduler.setWaitForTasksToCompleteOnShutdown(true);
          ????????threadPoolTaskScheduler.setAwaitTerminationSeconds(60);
          ????????log.info("創(chuàng)建定時(shí)任務(wù)調(diào)度線程池?end");
          ????????return?threadPoolTaskScheduler;
          ????}

          }

          ?

          2.2 項(xiàng)目啟動(dòng)時(shí)初始化定時(shí)任務(wù)

          @Slf4j
          @Component
          public?class?ScheduledTaskRunner?implements?ApplicationRunner?{
          ????@Autowired
          ????private?ScheduledTaskService?scheduledTaskService;

          ????@Override
          ????public?void?run(ApplicationArguments?args)?throws?Exception?{
          ????????log.info("----初始化定時(shí)任務(wù)開始----");
          ????????scheduledTaskService.initTask();
          ????????log.info("----初始化定時(shí)任務(wù)完成----");
          ????}
          }

          ?

          2.3 定時(shí)任務(wù)公共接口

          public?interface?ScheduledOfTask?extends?Runnable{

          ????void?execute();

          ????@Override
          ????default?void?run()?{
          ????????execute();
          ????}
          }

          ?

          2.4 創(chuàng)建兩個(gè)定時(shí)任務(wù)實(shí)現(xiàn)類

          @Component
          @Slf4j
          public?class?TaskJob1?implements?ScheduledOfTask{
          ????@Override
          ????public?void?execute()?{
          ????????log.info("執(zhí)行任務(wù)1?"+?LocalDateTime.now());
          ????}
          }

          ?

          @Component
          @Slf4j
          public?class?TaskJob2?implements?ScheduledOfTask{
          ????@Override
          ????public?void?execute()?{
          ????????log.info("執(zhí)行任務(wù)2?"+?LocalDateTime.now());
          ????}
          }

          ?

          2.5 定時(shí)任務(wù)管理接口

          public?interface?ScheduledTaskService{

          ????Boolean?start(ScheduledJob?scheduledJob);

          ????Boolean?stop(String?jobKey);

          ????Boolean?restart(ScheduledJob?scheduledJob);

          ????void?initTask();
          }

          ?

          2.6 定時(shí)任務(wù)管理實(shí)現(xiàn)類

          @Slf4j
          @Service
          public?class?ScheduledTaskServiceImpl?implements?ScheduledTaskService?{

          ????/**
          ?????*?可重入鎖
          ?????*/
          ????private?ReentrantLock?lock?=?new?ReentrantLock();

          ????/**
          ?????*?定時(shí)任務(wù)線程池
          ?????*/
          ????@Autowired
          ????private?ThreadPoolTaskScheduler?threadPoolTaskScheduler;

          ????/**
          ?????*?啟動(dòng)狀態(tài)的定時(shí)任務(wù)集合
          ?????*/
          ????public?Map?scheduledFutureMap?=?new?ConcurrentHashMap<>();

          ????@Autowired
          ????private?ScheduledJobService?scheduledJobService;

          ????@Override
          ????public?Boolean?start(ScheduledJob?scheduledJob)?{
          ????????String?jobKey?=?scheduledJob.getJobKey();
          ????????log.info("啟動(dòng)定時(shí)任務(wù)"+jobKey);
          ????????//添加鎖放一個(gè)線程啟動(dòng),防止多人啟動(dòng)多次
          ????????lock.lock();
          ????????log.info("加鎖完成");

          ????????try?{
          ????????????if(this.isStart(jobKey)){
          ????????????????log.info("當(dāng)前任務(wù)在啟動(dòng)狀態(tài)中");
          ????????????????return?false;
          ????????????}
          ????????????//任務(wù)啟動(dòng)
          ????????????this.doStartTask(scheduledJob);
          ????????}?finally?{
          ????????????lock.unlock();
          ????????????log.info("解鎖完畢");
          ????????}

          ????????return?true;
          ????}

          ????/**
          ?????*?任務(wù)是否已經(jīng)啟動(dòng)
          ?????*/
          ????private?Boolean?isStart(String?taskKey)?{
          ????????//校驗(yàn)是否已經(jīng)啟動(dòng)
          ????????if?(scheduledFutureMap.containsKey(taskKey))?{
          ????????????if?(!scheduledFutureMap.get(taskKey).isCancelled())?{
          ????????????????return?true;
          ????????????}
          ????????}
          ????????return?false;
          ????}

          ????@Override
          ????public?Boolean?stop(String?jobKey)?{
          ????????log.info("停止任務(wù)?"+jobKey);
          ????????boolean?flag?=?scheduledFutureMap.containsKey(jobKey);
          ????????log.info("當(dāng)前實(shí)例是否存在?"+flag);
          ????????if(flag){
          ????????????ScheduledFuture?scheduledFuture?=?scheduledFutureMap.get(jobKey);

          ????????????scheduledFuture.cancel(true);

          ????????????scheduledFutureMap.remove(jobKey);
          ????????}
          ????????return?flag;
          ????}

          ????@Override
          ????public?Boolean?restart(ScheduledJob?scheduledJob)?{
          ????????log.info("重啟定時(shí)任務(wù)"+scheduledJob.getJobKey());
          ????????//停止
          ????????this.stop(scheduledJob.getJobKey());

          ????????return?this.start(scheduledJob);
          ????}

          ????/**
          ?????*?執(zhí)行啟動(dòng)任務(wù)
          ?????*/
          ????public?void?doStartTask(ScheduledJob?sj){
          ????????log.info(sj.getJobKey());
          ????????if(sj.getStatus().intValue()?!=?1)
          ????????????return;
          ????????Class?clazz;
          ????????ScheduledOfTask?task;
          ????????try?{
          ????????????clazz?=?Class.forName(sj.getJobKey());
          ????????????task?=?(ScheduledOfTask)?SpringContextUtil.getBean(clazz);
          ????????}?catch?(ClassNotFoundException?e)?{
          ????????????throw?new?IllegalArgumentException("spring_scheduled_cron表數(shù)據(jù)"?+?sj.getJobKey()?+?"有誤",?e);
          ????????}
          ????????Assert.isAssignable(ScheduledOfTask.class,?task.getClass(),?"定時(shí)任務(wù)類必須實(shí)現(xiàn)ScheduledOfTask接口");
          ????????ScheduledFuture?scheduledFuture?=?threadPoolTaskScheduler.schedule(task,(triggerContext?->?new?CronTrigger(sj.getCronExpression()).nextExecutionTime(triggerContext)));
          ????????scheduledFutureMap.put(sj.getJobKey(),scheduledFuture);
          ????}

          ????@Override
          ????public?void?initTask()?{
          ????????List?list?=?scheduledJobService.list();
          ????????for?(ScheduledJob?sj?:?list)?{
          ????????????if(sj.getStatus().intValue()?==?-1)?//未啟用
          ????????????????continue;
          ????????????doStartTask(sj);
          ????????}
          ????}
          }

          ?

          2.7上面用到的獲取Bean的工具類SpringContextUtil

          @Component
          public?class?SpringContextUtil?implements?ApplicationContextAware?{


          ????private?static?ApplicationContext?applicationContext?=?null;
          ????@Override
          ????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
          ????????if(SpringContextUtil.applicationContext?==?null){
          ????????????SpringContextUtil.applicationContext??=?applicationContext;
          ????????}
          ????}

          ????public?static?ApplicationContext?getApplicationContext()?{
          ????????return?applicationContext;
          ????}

          ????public?static?Object?getBean(String?name){
          ????????return?getApplicationContext().getBean(name);
          ????}

          ????public?static??T?getBean(Class?clazz){
          ????????return?getApplicationContext().getBean(clazz);
          ????}

          ????public?static??T?getBean(String?name,Class?clazz){
          ????????return?getApplicationContext().getBean(name,?clazz);
          ????}
          }

          ?

          2.8?表操作對應(yīng)的一些類

          Pojo

          @Data
          @TableName("scheduled_job")
          public?class?ScheduledJob?{

          ????@TableId(value?=?"job_id",type?=?IdType.AUTO)
          ????private?Integer?jobId;

          ????private?String?jobKey;

          ????private?String?cronExpression;

          ????private?String?taskExplain;

          ????private?Integer?status;

          }

          ?ScheduledJobMapper

          1?public?interface?ScheduledJobMapper?extends?BaseMapper?{
          2?}

          ?

          ScheduledJobService

          public?interface?ScheduledJobService?extends?IService?{

          ????/**
          ?????*?修改定時(shí)任務(wù),并重新啟動(dòng)
          ?????*?@param?scheduledJob
          ?????*?@return
          ?????*/
          ????boolean?updateOne(ScheduledJob?scheduledJob);
          }
          @Service
          @Slf4j
          public?class?ScheduledJobServiceImpl?extends?ServiceImpl?implements?ScheduledJobService{

          ????@Autowired
          ????private?ScheduledTaskService?scheduledTaskService;

          ????@Override
          ????public?boolean?updateOne(ScheduledJob?scheduledJob)?{
          ????????if(updateById(scheduledJob))
          ????????????scheduledTaskService.restart(getById(scheduledJob.getJobId()));
          ????????return?true;
          ????}
          }

          ?

          2.9?修改定時(shí)任務(wù)的接口

          @RestController
          @RequestMapping("/job")
          public?class?ScheduledJobController?{

          ????@Autowired
          ????private?ScheduledJobService?scheduledJobService;

          ????@PostMapping(value?=?"/update")
          ????public?CallBackResult?update(HttpServletRequest?request,?ScheduledJob?scheduledJob){
          ?????????if(scheduledJobService.updateOne(scheduledJob))
          ?????????????return?new?CallBackResult(true,"修改成功");
          ?????????return?new?CallBackResult(false,"修改失敗");
          ????}

          }

          ?

          3、測試結(jié)果

          3.1 啟動(dòng)項(xiàng)目,看下定時(shí)任務(wù)的執(zhí)行結(jié)果,控制臺輸出結(jié)果

          ?

          ?

          我們可以看到任務(wù)1是每5秒執(zhí)行一次,任務(wù)2是12秒執(zhí)行一次

          ?

          3.2 修改任務(wù)1的cron參數(shù)或者狀態(tài)

          ?

          ?3.2.1 修改cron,執(zhí)行周期改為20秒執(zhí)行一次,狀態(tài)不變

          ?

          再看控制臺輸出結(jié)果,任務(wù)2沒變化,任務(wù)1由5秒一次變成了20秒一次了

          ?

          ?3.2.1 修改狀態(tài)

          ?

          ?

          ?

          ?

          ?再看控制臺輸出結(jié)果,任務(wù)2沒變化,任務(wù)1已經(jīng)不再執(zhí)行了

          ?

          最后

          第二種方式支持通過接口的方式去改動(dòng),并且不需要重啟,當(dāng)然啦,也可以直接在數(shù)據(jù)庫中添加或修改數(shù)據(jù)后重啟項(xiàng)目,配置更加靈活一點(diǎn)。

          如果是一個(gè)固定的需求,執(zhí)行周期一定不會變的了,推薦還是第一種寫法,畢竟簡單嘛。


          ? 作者?|??Jae1995

          來源 |??cnblogs.com/jae-tech/p/15402078.html


          加鋒哥微信:?java1239??
          圍觀鋒哥朋友圈,每天推送Java干貨!

          瀏覽 59
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  九七成人网 | 激情乱伦图片 | 青娱乐国产无码 | 黄色日韩欧美 | 一本道精品在线播放 |