最近有一个需求,客户需要在页面上选择某些信息,然后选择邮件接收人,然后设置每天哪个时间点发送邮件来新增一个定时任务,而且可以同时执行多个定时任务,还要求这些定时任务有启动、停止、修改的按钮。
去网上翻了一些文章,大部分都是触发了定时任务才能修改下次的执行时间,跟我的需求不符,然后借鉴了一些文章内容实现了自己的需求,接下来上代码。
注释都比较清楚,就不多做解释了。
/***
* 功能描述:定时任务调度类
*/
@Component
public class DynamicTimedTask {
private static final Logger logger = LoggerFactory.getLogger(DynamicTimedTask.class);
//创建map存放线程
public static ConcurrentHashMap<String, ScheduledFuture> map = new ConcurrentHashMap<String, ScheduledFuture>();
//利用创建好的调度类统一管理
//接受任务的返回结果
private ScheduledFuture<?> future;
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Autowired
private ProductMapper productMapper;
//线程池任务调度类,自定义的ThreadPoolTaskScheduler
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
executor.setPoolSize(20);
return executor;
}
/**
* 系统初始化启动所有定时任务
*/
@PostConstruct
public void init(){
//获取状态为开启的所有定时任务集合
List<TaskDto> list = productMapper.selectAllCron();
for (TaskDto taskDto : list) {
//参数我删除了一些,你可以根据自己的实体类里有什么添加参数
this.startCron(taskDto.getCron(),String.valueOf(taskDto.getId()));
System.out.println("定时任务"+String.valueOf(taskDto.getId())+"初始化启动成功");
}
}
/**
* 启动定时任务
*
* @return
* @param cron
* @param id
*/
public boolean startCron( String id,String cron) {
boolean flag = false;
//开启任务,获取线程话柄
future = threadPoolTaskScheduler.schedule(new SendEmail(id), new CronTrigger(cron));
//将线程话柄存入map集合,键为任务id,方便后续停止指定任务
map.put(id,future);
if (future != null) {
flag = true;
System.out.println("定时任务"+id+"已添加");
} else {
System.out.println("定时任务"+id+"添加失败");
}
return flag;
}
/**
* 停止定时任务
* @param id
*/
public boolean stopCron(String id) {
boolean flag = false;
//根据任务id获取话柄
ScheduledFuture scheduledFuture = map.get(id);
if (scheduledFuture != null) {
//停止任务
boolean cancel = scheduledFuture.cancel(true);
if (cancel) {
flag = true;
System.out.println("定时任务"+id+"已停止");
} else {
System.out.println("定时任务"+id+"停止失败");
}
} else {
flag = true;
}
return flag;
}
}
@Slf4j
@RestController
@RequestMapping("/task")
public class TaskController extends BaseController {
@Autowired
private ProductMapper productMapper;
@Autowired
private DynamicTimedTask dynamicTimedTask;
//新增定时任务
@PostMapping("/addJob")
public ApiResult add(@RequestBody @ApiIgnore Map<String, Object> map) throws Exception {
TaskDto taskDto = new TaskDto();
List<String> prdCodes = (List<String>) map.get("prdCodes");
List<String> recipientsList = (List<String>) map.get("recipientsList");
String date = (String) map.get("date");
//格式化时间
SimpleDateFormat sdf = new SimpleDateFormat( "HH:mm:ss" );
//时间转化为cron表达式,比如:09:00:00 转化为 0 0 9 * * ?
String cron = CronUtil.getCron(sdf.parse(date));
taskDto.setCron(cron);
taskDto.setPrdcodes(StringUtil.join(prdCodes,","));
taskDto.setRecipients(StringUtil.join(recipientsList,","));
//存入定时任务信息,存入数据库
int row = productMapper.insertJob(taskDto);
//插入定时任务信息成功则开启定时任务
if (row>0){
dynamicTimedTask.startCron(cron, String.valueOf(taskDto.getId()));
}
return null;
}
//停止定时任务
@PostMapping("/stopJob")
public void stop(@RequestBody @ApiIgnore Map<String, Object> map) {
String id = (String) map.get("id");
dynamicTimedTask.stopCron(id);
//根据id修改定时任务状态为停止,数据库表里有一个标识字段,"0"为启动,"1"为关闭
productMapper.stopTask(id);
}
//停止之后重新开启定时任务
@PostMapping("/startJob")
public void startJob(@RequestBody @ApiIgnore Map<String, Object> map) {
String id = (String) map.get("id");
//根据任务ID查询定时任务
TaskDto taskDto = productMapper.selectCron(id);
if (taskDto != null) {
dynamicTimedTask.startCron(taskDto.getCron(), id);
}
}
//更新定时任务
@PostMapping("/updateJob")
public void updateJob(@RequestBody @ApiIgnore Map<String, Object> map) throws ParseException {
Map<String, Object> params = new HashMap<>();
String id = (String) map.get("id");
String date = (String) map.get("date");
//格式化时间
SimpleDateFormat sdf = new SimpleDateFormat( "HH:mm:ss" );
//时间转化为cron表达式,比如:09:00:00 转化为 0 0 9 * * ?
String cron = CronUtil.getCron(sdf.parse(date));
params.put("id", id);
params.put("cron",cron);
//先停止任务
dynamicTimedTask.stopCron(id);
//更新定时任务的cron
int row = productMapper.updateTask(params);
//更新成功则重新执行任务
if (row>0){
dynamicTimedTask.startCron(cron,id);
}
}
//删除定时任务
@PostMapping("/deleteJob")
public void deleteJob(@RequestBody @ApiIgnore Map<String, Object> map) {
String id = (String) map.get("id");
dynamicTimedTask.stopCron(id);
//删除定时任务
productMapper.deleteTask(id);
}
}
总的来说,更新cron并实时生效的逻辑就是先把定时任务停止,然后拿到更改后的cron,再重新开启定时任务.
下面是发送邮件的测试类,实现Runnable.
/***
* 功能描述:发送邮件代码
*/
public class SendEmail implements Runnable {
private String id;
public SendEmail(String id) {
this.id = id;
}
@Override
public void run() {
System.out.println("发送成功" + id);
}
}
最后放上cron的工具类,我的需求是每天某个时间点,大家可以根据自己需求更改dateFormat表达式.
/***
* 功能描述:日期转换cron表达式
*/
public class CronUtil {
/***
* 功能描述:日期转换cron表达式
* @param date
* @param dateFormat
* @return
*/
public static String formatDateByPattern(Date date, String dateFormat){
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
String formatTimeStr = null;
if (date != null) {
formatTimeStr = sdf.format(date);
}
return formatTimeStr;
}
/***
* @param date : 时间点
* @return
*/
public static String getCron(java.util.Date date){
String dateFormat="ss mm HH * * ?";
return formatDateByPattern(date, dateFormat);
}
}
经自己测试,实时更新cron是成功的.
如有错误之处,望各位大佬指正.