Skip to content

Latest commit

 

History

History
439 lines (345 loc) · 14.5 KB

定时任务.md

File metadata and controls

439 lines (345 loc) · 14.5 KB

定时任务

Spring(固定任务)

Spring.xml

spring.xml中<beans 中添加

http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.1.xsd

添加扫描包

<task:annotation-driven/>
<context:component-scan base-package="com.ruiger.common.task"/>

创建定时任务

package com.ruiger.common.task;

import java.util.Date;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component 
public class AttendTask {
    @Autowired
    IRecordService recordService;

    private static Logger logger = Logger.getLogger(AttendTask.class);

    // 每小时1分执行
    @Scheduled(cron="0 1 0/1 * * ? ") 
    public void saveRecordIn1h(){
        System.out.println("test")
    }
}

时间设置参看cron表达式

Spring(动态任务)

Spring.xml

spring.xml中<beans 中添加

http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.1.xsd

添加扫描包

<task:annotation-driven/>
<context:component-scan base-package="com.ruiger.common.task"/>

实现类

package com.ruiger.common.task;

import java.util.Date;

import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import com.ruiger.common.utils.StringUtil;

@Component
@Lazy(false)
@EnableScheduling
public class TestTask implements SchedulingConfigurer {
    public static String cron = "10 0/1 * * * ? ";

    public void setCron(final String cron){
        this.cron = cron;
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(new Runnable() {

            @Override
            public void run() {
                // 需要实现的方法
                System.out.println(String.format("测试1:%s", StringUtil.getDateStr(new Date(), "yyyy-MM-dd HH:mm:ss")));
            }
        }, new Trigger(){  
            @Override  
            public Date nextExecutionTime(TriggerContext triggerContext) {  
                //任务触发,可修改任务的执行周期   
                CronTrigger trigger = new CronTrigger(cron);  
                Date nextExec = trigger.nextExecutionTime(triggerContext);  
                return nextExec;    
            }  
        });  
    }
}

更改时间

TestTask m = new TestTask();
m.setCron("15 0/1 * * * ? ");

测试/缺陷:

修改后,本分钟内不会执行新任务(即使时间未到),下一分钟会执行旧新两次任务

每分钟一次的定时任务
10 0/1 * * * ? → 0 29 11 * * ? 
修改后,本分钟内不会执行新任务(即使时间未到),下一分钟会执行旧新两次任务
测试1:2019-11-14 11:38:10
测试1:2019-11-14 11:39:10
测试1:2019-11-14 11:39:15



按秒间隔的定时任务
0/10 * * * * ? → 0/15 * * * * ? 
修改后,会再执行一次旧任务,然后再应用新时间
测试1:2019-11-14 12:01:50
测试1:2019-11-14 12:02:00
测试1:2019-11-14 12:02:15

ThreadPoolTaskSchedule

package com.ruiger.common.task;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruiger.common.utils.StringUtil; 

@RestController
@Component
public class TestTask { 

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler; 

    private ScheduledFuture<?> future; 

    @Bean
    public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
        return new ThreadPoolTaskScheduler();
    } 

    @RequestMapping("/startCron")
    public String startCron() {

        if(future == null || future.isCancelled()) {
            future = threadPoolTaskScheduler.schedule(new MyRunnable(), new CronTrigger("0/10 * * * * *"));
            System.out.println("DynamicTask.startCron()");
        }
        return "startCron";
    } 

    @RequestMapping("/stopCron")
    public String stopCron() {

        if (future != null) {
            future.cancel(true);
        }
        System.out.println("DynamicTask.stopCron()");
        return "stopCron";
    }

    @RequestMapping("/changeCron10")
    public String startCron10() {

        stopCron();// 先停止,在开启.
        future = threadPoolTaskScheduler.schedule(new MyRunnable(), new CronTrigger("0/15 * * * * *"));
        System.out.println("DynamicTask.startCron10()");
        return "changeCron10";
    }

    private class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println(String.format("test1:%s", StringUtil.getDateStr(new Date(), "yyyy-MM-dd HH:mm:ss")));
        }
    } 
}

Quartz

jar

quartz-2.3.2.jar quartz-jobs-2.3.2.jar

spring.xml

<!-- 初始化Scheduler -->
<bean id="schedulerFactoryBean"  class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />

实现类 SchedulerUtils.java

package com.ruiger.common.quartz;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;

@Component
public abstract class SchedulerUtils implements ApplicationContextAware {

    private static final Logger logger= LoggerFactory.getLogger(SchedulerUtils.class);

    private static ApplicationContext appContext;
    public static SchedulerFactoryBean schedulerFactoryBean = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (appContext == null) {
            appContext = applicationContext;
        }
        // spring 启动时初始化SchedulerFactoryBean
        schedulerFactoryBean = (SchedulerFactoryBean) appContext.getBean(SchedulerFactoryBean.class);

        logger.info("setApplicationContext");
    }

    public static <T extends Job> void addJob(Object id, String cron, String groupName, Class<T> clazz) throws SchedulerException {
        addJob(id, cron, groupName, clazz, null);
    }

    /**
		* 添加任务
		* 
		* @param scheduleJob
		* @throws SchedulerException
		*/
    public static <T extends Job> void addJob(Object id, String cron, String groupName, Class<T> clazz, Map<String, Object> extras) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(id.toString(), groupName);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        // 不存在,创建一个
        if (null == trigger) {
            JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(id.toString(), groupName).build();
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
            TriggerBuilder<CronTrigger> triggerBuilder = TriggerBuilder.newTrigger().withDescription(id.toString())
                .withIdentity(id.toString(), groupName).withSchedule(scheduleBuilder);
            // 在trigger中额外数据
            // 当trigger中已有键值时,triggerBuilder.usingJobData(JobDataMap) 无效??
            if(extras != null) {
                // triggerBuilder.usingJobData(new JobDataMap(extras));
                for(Map.Entry<String, Object> entity : extras.entrySet()) {
                    triggerBuilder.usingJobData(entity.getKey(), entity.getValue().toString());
                }
            }
            trigger = triggerBuilder.build();

            scheduler.scheduleJob(jobDetail, trigger);

            logger.info(String.format("quartz job new: %s %s %s", id, cron, groupName));
        } else {
            // Trigger已存在,那么更新相应的定时设置
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);

            // 按新的cronExpression表达式重新构建trigger
            TriggerBuilder<CronTrigger> triggerBuilder = trigger.getTriggerBuilder().withIdentity(triggerKey)
                .withSchedule(scheduleBuilder);
            if(extras != null) {
                // triggerBuilder.usingJobData(new JobDataMap(extras));
                for(Map.Entry<String, Object> entity : extras.entrySet()) {
                    triggerBuilder.usingJobData(entity.getKey(), entity.getValue().toString());
                }
            }
            trigger = triggerBuilder.build();

            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);

            logger.info(String.format("quartz job update: %s %s %s", id, cron, groupName));
        }
    }

    public static void delJob(Object id, String groupName) throws Exception {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(id.toString(), groupName);
        if(jobKey != null) {
            scheduler.deleteJob(jobKey);
            logger.info(String.format("quartz job delete: %s %s", id, groupName));
        }else {
            logger.info(String.format("quartz job delete: %s %s, but the job is not exist", id, groupName));
        }
    }

    public static String getCron(Date date) {
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        return String.format("%d %d %d %d %d ? %d", c.get(Calendar.SECOND), c.get(Calendar.MINUTE), c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.DATE), c.get(Calendar.MONTH) + 1, c.get(Calendar.YEAR));
    }

    public static String getCron(Date date, int advMinutes) {
        Calendar c = Calendar.getInstance();
        c.setTimeInMillis(date.getTime() - advMinutes * 60 * 1000);
        return String.format("%d %d %d %d %d ? %d", c.get(Calendar.SECOND), c.get(Calendar.MINUTE), c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.DATE), c.get(Calendar.MONTH) + 1, c.get(Calendar.YEAR));
    }

    public static String getMinutesStr(int minutes) {
        int h = minutes / 60;
        int m = minutes % 60;
        return (h > 0 ? (String.format("%d小时", h)) : "") + (h == 0 || m > 0 ? (String.format("%d分钟", m)) : "");
    }

}

TodoJob

实现job接口,任务触发时execute(JobExecutionContext context)方法被回调

package com.ruiger.common.quartz;

import java.util.Map;

import javax.annotation.PostConstruct;

import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ruiger.common.msg.MsgHttpUtils;
import com.ruiger.model.space.oa.Todo;
import com.ruiger.service.space.oa.ITodoService;

@Component
public class TodoJob implements Job {
    private static final Logger logger= LoggerFactory.getLogger(TodoJob.class);

    @Autowired
    private ITodoService _todoService;
    private static ITodoService todoService;

    @PostConstruct 
    public void initService() {
        todoService = _todoService;
    }

    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobKey jobKey = context.getJobDetail().getKey();
        String id = jobKey.getName();
        String groupName = jobKey.getGroup();
        JobDataMap m = context.getTrigger().getJobDataMap();
        Map<String, Object> map = m.getWrappedMap();

        // 查询待办,如待办仍存在,发送提示通讯提醒,如不存在,说明文件已办,无需提醒
        Todo todo = todoService.selectById(id);
        if(todo != null) {

            if(groupName.equals(TodoSchedulerUtils.GROUP_SIGN)) {
                if(todo.getIsSign() == 0) {
                    // 未签收待办
                    logger.info(String.format("on todo notice, todo is unsigned: %s %s %s", groupName, todo.getId(), todo.getTodoTitle()));

                    // 发送即时消息
                    String title = String.format("您的待办【%s】已%s未签收,请尽快签收办理", todo.getTodoTitle(), SchedulerUtils.getMinutesStr(map.containsKey("time") ? Integer.valueOf(map.get("time").toString()) : 0));
                    MsgHttpUtils.pushMsgByCode(todo.getUserCode(), title, todo.getTodoTitle());

                    // 再次设置签到提醒
                    TodoSchedulerUtils.addSignNotice(todo, map);
                }else {
                    logger.info(String.format("on todo notice, todo is signed, skiped: %s %s %s", groupName, todo.getId(), todo.getTodoTitle()));
                }
            }else if(groupName.equals(TodoSchedulerUtils.GROUP)) {
                // 未办理
                String title = String.format("您的待办【%s】已%s未办理,请尽快办理", todo.getTodoTitle(), SchedulerUtils.getMinutesStr(map.containsKey("time") ? Integer.valueOf(map.get("time").toString()) : 0));

                // 发送即时消息
                MsgHttpUtils.pushMsgByCode(todo.getUserCode(), title, todo.getTodoTitle());

                // 再次设置办理提醒
                TodoSchedulerUtils.addHandleNotice(todo, map);
            }

        }else {
            logger.info(String.format("on todo notice: %s %s, todo is over(todo is null)", groupName, id));
        }
    }
}