在我们平时开发的项目中,定时任务基本属于必不可少的功能,那大家都是怎么做的呢?但我知道的大多都是静态定时任务实现。

基于注解来创建定时任务非常简单,只需几行代码便可完成。实现如下:

 

[Java] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
<font style="color:rgb(77, 77, 77)"><font face="&quot;"><font style="font-size:16px">@Configuration
@EnableScheduling
public class SimpleScheduleTask {
  
    //10秒钟执行一次
    @Scheduled(cron = "0/10 * * * * ?")
    private void tasks() {
        System.out.println("【定时任务】 每10秒执行一次!");
    }
}
</font></font></font>

 

Cron表达式参数分别表示(从左到右):
秒(0~59) 如0/5表示每5秒
分(0~59)
时(0~23)
日(0~31) 月的某一天
月(0~11)
周几( 可填1-7 或 SUN/MON/TUE/WED/THU/FRI/SAT)

就上面几行代码,就能搞定一个定时任务。显然,使用Scheduled 确实特别的方便,但有很大的缺点和局限,就是当我们调整了执行计划的时间时,需要重启服务才能生效,这就有些不方便。为了达到实时生效的效果,可以通过数据库来动态实现定时任务。

基于数据库的动态定时任务实现

将定时任务配置在数据库,启动项目的时候,用mybatis读取数据库,实例化对象,并设定定时任务。如果需要新增,减少,修改定时任务,仅需要修改数据库资料,并重启项目即可,无需改代码。

 

[Java] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<font style="color:rgb(77, 77, 77)"><font face="&quot;"><font style="font-size:16px">Lazy(value = false)
@Component
public class ScheduleTask implements SchedulingConfigurer {
  
    protected static Logger logger = LoggerFactory.getLogger(ScheduleTask.class);
    private SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  
    @Autowired
    private ScheduleTaskMapper scheduleTaskMapper;
  
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        List<ScheduleTask> tasks = getAllScheduleTasks();
        logger.info("【定时任务启动】 启动任务数:"+tasks.size()+"; time="+sdf.format(new Date()));
  
        //校验数据
        checkDataList(tasks);
        //通过校验的数据执行定时任务
        int count = 0;
        if(tasks.size()>0) {
            for (int i = 0; i < tasks.size(); i++) {
                try {
                    taskRegistrar.addTriggerTask(getRunnable(tasks.get(i)), getTrigger(tasks.get(i)));
                    count++;
                } catch (Exception e) {
                    logger.error("task start error:" + tasks.get(i).getClassName() + ";" + tasks.get(i).getMethodName() + ";" + e.getMessage());
                }
            }
        }
        logger.info("started task number="+count+"; time="+sdf.format(new Date()));
    };
  
  /**
  * 获取要执行的所有任务
  * @return
  */
    private List<ScheduleTask> getAllScheduleTasks() {
        ScheduleTaskExample example=new ScheduleTaskExample();
        example.createCriteria().andIsDeleteEqualTo((byte) 0);
        return scheduleTaskMapper.selectByExample(example);
    }
   
  /**
  * 获取Runnable
  *
  * @param task
  * @return
  */
    private Runnable getRunnable(ScheduleTask task){
        return new Runnable() {
            @Override
            public void run() {
                try {
                    Object obj = SpringUtil.getBean(task.getClassName());
                    Method method = obj.getClass().getMethod(task.getMethodName(),null);
                    method.invoke(obj);
                } catch (InvocationTargetException e) {
                    logger.error("refect exception:"+task.getClassName()+";"+task.getMethodName()+";"+ e.getMessage());
                } catch (Exception e) {
                    logger.error(e.getMessage());
                }
            }
        };
    }
  
  /**
  * 获取Trigger
  *
  * @param task
  * @return
  */
    private Trigger getTrigger(ScheduleTask task){
        return new Trigger() {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext) {
                //将Cron 0/1 * * * * ?
                CronTrigger trigger = new CronTrigger(task.getCron());
                Date nextExec = trigger.nextExecutionTime(triggerContext);
                return nextExec;
            }
        };
    }
   
  /**
  * 校验数据
  *
  * @param list
  * @return
  */
    private List<ScheduleTask> checkDataList(List<ScheduleTask> list) {
        String msg="";
        for(int i=0;i<list.size();i++){
            if(!checkOneData(list.get(i)).equalsIgnoreCase("ok")){
                msg+=list.get(i).getTaskName()+";";
                list.remove(list.get(i));
                i--;
            };
        }
        if(!StringUtils.IsEmpty(msg)){
            msg="未启动的任务:"+msg;
            logger.error(msg);
        }
        return list;
    }
  
  /**
  * 按每一条校验数据
  *
  * @param task
  * @return
  */
    private String checkOneData(ScheduleTask task){
        String result="ok";
        Class cal= null;
        try {
            cal = Class.forName(task.getClassName());
            Object obj =SpringUtil.getBean(cal);
            Method method = obj.getClass().getMethod(task.getMethodName(),null);
            String cron=task.getCron();
            if(StringUtils.isBlank(cron)){
                result="no found the cron:"+task.getTaskName();
                logger.error(result);
            }
        } catch (ClassNotFoundException e) {
            result="not found the class:"+task.getClassName()+ e.getMessage();
            logger.error(result);
        } catch (NoSuchMethodException e) {
            result="not found the method:"+task.getClassName()+";"+task.getMethodName()+";"+ e.getMessage();
            logger.error(result);
        } catch (Exception e) {
          logger.error(e.getMessage());
         }
        return result;
    }
</font></font></font>

 

数据库配置

<ignore_js_op>SpringBoot基于数据库的定时任务实现_ide

运行的结果

<ignore_js_op>SpringBoot基于数据库的定时任务实现_ide_02

这样我们可以通过直接修改数据库,执行周期就会改变,并且不需要我们重启应用,十分方便。

更多技术资讯可关注:itheimaGZ获取