Spring集成Quartz持久化+动态更改定时任务+Sql Server数据库+发送邮件
本文采用双数据源配置:链接: springboot配置多数据源
1、首先引入pom依赖
<!--spring quartz依赖-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
2、默认使用org.quartz下的quartz.properties配置文件,如果在resources下创建quartz.properties会使用创建的配置文件。
#调度器名称
org.quartz.scheduler.instanceName: MyScheduler
#如果使用集群,instanceId必须唯一,设置成AUTO
org.quartz.scheduler.instanceId = AUTO
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
#是否使用集群
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval=10000
# jobStore 持久化配置
#存储方式使用JobStoreTX,也就是数据库
org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 使用quartz.properties,不使用默认配置
org.quartz.jobStore.useProperties:false
#数据库中quartz表的表名前缀
org.quartz.jobStore.tablePrefix:QRTZ_
org.quartz.jobStore.dataSource:myDS
#配置数据源
org.quartz.dataSource.myDS.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
org.quartz.dataSource.myDS.URL=jdbc:sqlserver://127.0.0.1:1433;SelectMethod=cursor;Databasename=attendance
org.quartz.dataSource.myDS.user=xxxxxx
org.quartz.dataSource.myDS.password:xxxxx
org.quartz.dataSource.myDS.validationQuery=select 0
本文连接池采用hikaricp,如果没有下面配置加载myDS数据源会报错
org.quartz.dataSource.myDS.provider=hikaricp
发送邮件配置
# Email (MailProperties) 邮件属性
# SMTP服务器主机。 例如`smtp.example.com`
spring.mail.host=smtp.example.com
# 登录SMTP服务器的用户。
spring.mail.username=发送人邮箱账号
# 登录SMTP服务器的密码。
spring.mail.password=发送人邮箱密码
# SMTP服务器使用的协议。
spring.mail.protocol=smtp
# SMTP服务器端口。
spring.mail.properties.mail.smtp.port=端口号
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.ssl.enable=true
# 默认MimeMessage编码。
spring.mail.default-encoding= UTF-8
3、quartz要想持久化需要先在数据库创建quartz的表,这里大家可以根据org.quartz.impl.jdbcjobstore下面找到自己数据库对应的建表语句。
4、创建一个关于定时器的表格,用于页面上动态操作的表,建表语句如下
USE [attendance]
GO
/****** Object: Table [dbo].[att_quartz_job] Script Date: 02/28/2020 15:52:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[att_quartz_job](
[id] [int] IDENTITY(1,1) NOT NULL,
[job_name] [varchar](512) NOT NULL,
[job_group] [varchar](512) NOT NULL,
[job_cron] [varchar](512) NOT NULL,
[job_class_path] [varchar](1024) NOT NULL,
[job_data_map] [varchar](1024) NULL,
[job_status] [varchar](2) NOT NULL,
[job_describe] [varchar](1024) NULL,
[create_time] [datetime2](0) NOT NULL,
[update_time] [datetime2](0) NOT NULL,
PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
CONSTRAINT [att_quartz_job_job_name_b3b1d12f_uniq] UNIQUE NONCLUSTERED
(
[job_name] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'主键ID' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'att_quartz_job', @level2type=N'COLUMN',@level2name=N'id'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'任务名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'att_quartz_job', @level2type=N'COLUMN',@level2name=N'job_name'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'任务组名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'att_quartz_job', @level2type=N'COLUMN',@level2name=N'job_group'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'时间表达式' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'att_quartz_job', @level2type=N'COLUMN',@level2name=N'job_cron'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'类路径,全类型' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'att_quartz_job', @level2type=N'COLUMN',@level2name=N'job_class_path'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'传递map参数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'att_quartz_job', @level2type=N'COLUMN',@level2name=N'job_data_map'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'状态:1启用 0停用' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'att_quartz_job', @level2type=N'COLUMN',@level2name=N'job_status'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'任务功能描述' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'att_quartz_job', @level2type=N'COLUMN',@level2name=N'job_describe'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'创建时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'att_quartz_job', @level2type=N'COLUMN',@level2name=N'create_time'
GO
EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'修改时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'att_quartz_job', @level2type=N'COLUMN',@level2name=N'update_time'
GO
ALTER TABLE [dbo].[att_quartz_job] ADD DEFAULT (NULL) FOR [job_data_map]
GO
ALTER TABLE [dbo].[att_quartz_job] ADD DEFAULT (NULL) FOR [job_describe]
GO
ALTER TABLE [dbo].[att_quartz_job] ADD DEFAULT (getdate()) FOR [create_time]
GO
ALTER TABLE [dbo].[att_quartz_job] ADD DEFAULT (getdate()) FOR [update_time]
GO
5、编写配置文件,文本配置文件由三个组成,分别是InitStratSchedule.java和MyJobFactory.java和AdaptableJobFactory.java。其中
InitStratSchedule.java:查询att_quartz_job中存储的定时任务信息,初始化调度器。
MyJobFactory.java:创建Job实例,并且将job注入到spring的IOC中。
AdaptableJobFactory.java:quartz包中有这个类,但是目前不是很清楚为啥要单独在创建一个,集成了JobFactory类,做new Job的实例
InitStratSchedule.java
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSONObject;
import com.XXX.attendance.entity.quartz.JobDTO;
import com.XXX.attendance.service.quartz.IJobService;
import com.XXX.attendance.util.BaseJob;
/**
* <p>Description:这个类用于启动SpringBoot时,加载作业。run方法会自动执行。 另外可以使用 ApplicationRunner<p>
* @author oldw
*/
@Component
public class InitStartSchedule implements CommandLineRunner {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
// service接口
@Autowired
private IJobService jobService;
@Autowired
private MyJobFactory myJobFactory;
@Override
public void run(String... args) throws Exception {
// 用于程序启动时加载定时任务,并执行已启动的定时任务(只会执行一次,在程序启动完执行)
// 查询job状态为启用的
JobDTO jobDtoParam = new JobDTO();
jobDtoParam.setJobStatus("1");
// 查询job集合
List<JobDTO> jobList = jobService.querySysJobList(jobDtoParam);
if (null == jobList || jobList.size() == 0) {
logger.info("系统启动,没有需要执行的任务... ...");
}
// 通过SchedulerFactory获取一个调度器实例
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
// 如果不设置JobFactory,Service注入到JobTask类中会报空指针
scheduler.setJobFactory(myJobFactory);
// 启动调度器
scheduler.start();
for (JobDTO jobDto : jobList) {
JobDetail jobDetail = buildJobDetail(jobDto);
// 表达式调度构建器(即任务执行的时间)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobDto.getJobCron());
// 按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobDto.getJobName(), jobDto.getJobGroup())
.withSchedule(scheduleBuilder).startNow().build();
if (!scheduler.checkExists(jobDetail.getKey())) {
try {
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
logger.error("\n创建定时任务失败" + e);
throw new Exception("创建定时任务失败");
}
}
}
}
/**
* @Description: 构建JobDetail
*
* @throws Exception
* @author oldw
*/
@SuppressWarnings("unchecked")
private JobDetail buildJobDetail(JobDTO jobDto) throws Exception {
String jobName = jobDto.getJobName();
String jobGroup = jobDto.getJobGroup();
// 构建job
JobDetail jobDetail = JobBuilder.newJob(getClass(jobDto.getJobClassPath()).getClass())
.withIdentity(jobName, jobGroup).build();
if (StringUtils.isNotEmpty(jobDto.getJobDataMap())) {
JSONObject dataMapObj = JSONObject.parseObject(jobDto.getJobDataMap());
Map<String, Object> dataMap = (Map<String, Object>) dataMapObj.get("data");
for (Map.Entry<String, Object> m : dataMap.entrySet()) {
jobDetail.getJobDataMap().put(m.getKey(), m.getValue());
}
}
return jobDetail;
}
public static BaseJob getClass(String classname) throws Exception {
Class<?> c = Class.forName(classname);
return (BaseJob) c.newInstance();
}
}
MyJobFactory.java
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.stereotype.Component;
/**
* <p>Description:将Quartz Job的对象注入到spring<p>
* @author oldw
*/
@Component
public class MyJobFactory extends AdaptableJobFactory{
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
// 通过该方法将JobDetail注入到IOC中,这样Job类中才能注入server接口。(原因:job是在quartz中创建实例的,只有通过AdaptableJobFactory类自己去创建实例,创建之后的实例要托管到spring中,使用的就是AutowireCapableBeanFactory)
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
AdaptableJobFactory.java
import java.lang.reflect.Method;
import org.quartz.Job;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.scheduling.quartz.DelegatingJob;
import org.springframework.util.ReflectionUtils;
/**
* <p>Description: 将Quartz Job的对象注入到spring:quartz包中有这个类,暂时不知道为什么还要创建一个<p>
*
* @author oldw
*/
public class AdaptableJobFactory implements JobFactory {
@Override
public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
return newJob(bundle);
}
public Job newJob(TriggerFiredBundle bundle) throws SchedulerException {
try {
// 返回Job实例
Object jobObject = createJobInstance(bundle);
return adaptJob(jobObject);
} catch (Exception ex) {
throw new SchedulerException("Job instantiation failed", ex);
}
}
// 通过反射的方式创建实例
@SuppressWarnings("rawtypes")
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Method getJobDetail = bundle.getClass().getMethod("getJobDetail");
Object jobDetail = ReflectionUtils.invokeMethod(getJobDetail, bundle);
Method getJobClass = jobDetail.getClass().getMethod("getJobClass");
Class jobClass = (Class) ReflectionUtils.invokeMethod(getJobClass, jobDetail);
return jobClass.newInstance();
}
protected Job adaptJob(Object jobObject) throws Exception {
if (jobObject instanceof Job) {
return (Job) jobObject;
} else if (jobObject instanceof Runnable) {
return new DelegatingJob((Runnable) jobObject);
} else {
throw new IllegalArgumentException("Unable to execute job class [" + jobObject.getClass().getName()
+ "]: only [org.quartz.Job] and [java.lang.Runnable] supported.");
}
}
}
6、工具类:工具类中主要是操作定时任务的,包括新增定时任务,暂停定时任务,删除定时任务等等
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.fastjson.JSONObject;
/**
* <p>Description: Quartz工具类<p>
* @author oldw
* @date 2020年2月14日
*/
public class SchedulerUtil {
private static Logger logger = LoggerFactory.getLogger(SchedulerUtil.class);
/**
* @Description: 新增定时任务
*
* @param jobClassName 类路径
* @param jobName 任务名称
* @param jobGroupName 组别
* @param cronExpression Cron表达式
* @param jobDataMap 需要传递的参数
* @throws Exception
* @author oldw
* @date 2020年2月28日
*/
@SuppressWarnings("unchecked")
public static void addJob(String jobClassName,String jobName, String jobGroupName, String cronExpression,String jobDataMap) throws Exception {
// 通过SchedulerFactory获取一个调度器实例
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
// 启动调度器
scheduler.start();
// 构建job信息
JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass())
.withIdentity(jobName, jobGroupName).build();
// JobDataMap用于传递任务运行时的参数,比如定时发送邮件,可以用json形式存储收件人等等信息
if (StringUtils.isNotEmpty(jobDataMap)) {
JSONObject jb = JSONObject.parseObject(jobDataMap);
Map<String, Object> dataMap =(Map<String, Object>) jb.get("data");
for (Map.Entry<String, Object> m:dataMap.entrySet()) {
jobDetail.getJobDataMap().put(m.getKey(),m.getValue());
}
}
// 表达式调度构建器(即任务执行的时间)
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
// 按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroupName)
.withSchedule(scheduleBuilder).startNow().build();
try {
scheduler.scheduleJob(trigger);
} catch (SchedulerException e) {
logger.error("\n创建定时任务失败" + e);
throw new Exception("创建定时任务失败");
}
}
/**
* 停用一个定时任务
* @param jobName 任务名称
* @param jobGroupName 组别
* @throws Exception
*/
public static void jobPause(String jobName, String jobGroupName) throws Exception {
// 通过SchedulerFactory获取一个调度器实例
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
scheduler.pauseJob(JobKey.jobKey(jobName, jobGroupName));
}
/**
* 启用一个定时任务
* @param jobName 任务名称
* @param jobGroupName 组别
* @throws Exception
*/
public static void jobresume(String jobName, String jobGroupName) throws Exception {
// 通过SchedulerFactory获取一个调度器实例
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
scheduler.resumeJob(JobKey.jobKey(jobName, jobGroupName));
}
/**
* 删除一个定时任务
* @param jobName 任务名称
* @param jobGroupName 组别
* @throws Exception
*/
public static void jobdelete(String jobName, String jobGroupName, String jobCron) throws Exception {
// 通过SchedulerFactory获取一个调度器实例
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
// 停止触发器
scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
// 移除触发器
scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroupName));
// 删除任务
scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
}
/**
* 更新定时任务表达式
* @param jobName 任务名称
* @param jobGroupName 组别
* @param cronExpression Cron表达式
* @throws Exception
*/
public static void jobReschedule(String jobName, String jobGroupName, String cronExpression) throws Exception {
try {
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).startNow().build();
// 按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
} catch (SchedulerException e) {
System.out.println("更新定时任务失败" + e);
throw new Exception("更新定时任务失败");
}
}
/**
* 检查Job是否存在
* @throws Exception
*/
public static Boolean isResume(String jobName, String jobGroupName) throws Exception {
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
JobKey jobKey = new JobKey(jobName, jobGroupName);
Boolean state = scheduler.checkExists(jobKey);
return state;
}
/**
* 暂停所有任务
* @throws Exception
*/
public static void pauseAlljob() throws Exception {
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler scheduler = sf.getScheduler();
scheduler.pauseAll();
}
/**
* 唤醒所有任务
* @throws Exception
*/
public static void resumeAlljob() throws Exception {
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
sched.resumeAll();
}
/**
* 获取Job实例
* @param classname
* @return
* @throws Exception
*/
public static BaseJob getClass(String classname) throws Exception {
try {
Class<?> c = Class.forName(classname);
return (BaseJob) c.newInstance();
} catch (Exception e) {
throw new Exception("类["+classname+"]不存在!");
}
}
}
BaseJob
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* <p>Description: <p>
* @author oldw
* @date 2020年2月14日
*/
public interface BaseJob extends Job{
public void execute(JobExecutionContext context) throws JobExecutionException;
}
发送邮件的工具类SendMailUtil
import java.util.Date;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
import com.XXX.attendance.entity.quartz.MailDTO;
/**
* <p>Description: 发送邮件工具类。连接服务器mail.XXX.com,协议smtp,<p>
* @author wangb22
* @date 2020年2月21日
*/
@Component
public class SendMailUtil {
private final static Logger LOGGER = LoggerFactory.getLogger(SendMailUtil.class);
@Autowired
public JavaMailSender javaMailSender;
/**
* @Description: 发送文本邮件
*
* @param sender 发送者
* @param receiver 接收者,如果是多人,使用数组的形式
* @param subject 发送标题
* @param text 发送内容
* @author oldw
* @date 2020年2月21日
*/
public void sendMail(MailDTO mailDto) {
// 校验参数是否正确
verificationMailDto(mailDto);
SimpleMailMessage message = BuildSimpleMailMessage(mailDto);
try {
javaMailSender.send(message);
LOGGER.info("文本邮件发送成功!");
} catch (Exception e) {
LOGGER.error("文本邮件发送异常!", e);
}
}
/**
* @Description: 校验是否字段是否为空,如果为空,程序停止
*
* @param com.XXX.attendance.entity.quartz.MailDTO
* @author wangb22
* @date 2020年2月24日
*/
private void verificationMailDto(MailDTO mailDto) {
if (StringUtils.isEmpty(mailDto.getSender())) {LOGGER.error("发送邮件失败:{}为空",mailDto.getSender());return;}
if (StringUtils.isEmpty(mailDto.getReceiver())) {LOGGER.error("发送邮件失败:{}为空",mailDto.getReceiver());return;}
if (StringUtils.isEmpty(mailDto.getSubject())) {LOGGER.error("发送邮件失败:{}为空",mailDto.getSubject());return;}
if (StringUtils.isEmpty(mailDto.getText())) {LOGGER.error("发送邮件失败:{}为空",mailDto.getText());return;}
}
/**
* @Description: 构建SimpleMailMessage
*
* @param com.XXX.attendance.entity.quartz.MailDTO
* @author wangb22
* @date 2020年2月24日
*/
private SimpleMailMessage BuildSimpleMailMessage(MailDTO mailDto) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(mailDto.getSender());
String [] receiverArr= mailDto.getReceiver().split(",");
message.setTo(receiverArr);
message.setSentDate(new Date());// 日期
message.setSubject(mailDto.getSubject());// 标题
message.setText(mailDto.getText());// 内容
return message;
}
}
7、service的接口和实现类
import java.util.List;
import com.XXX.attendance.entity.quartz.JobDTO;
import com.XXX.attendance.exception.BizException;
/**
* <p>Description: quartz定时任务接口<p>
*
* @author oldw
* @date 2020年2月13日
*/
public interface IJobService {
/**
* @Description: 查询Quartz的定时任务列表
*
* @param com.XXX.attendance.entity.quartz.JobDTO
* @return java.util.List<com.XXX.attendance.entity.quartz.JobDTO>
* @throws BizException
* @author oldw
* @date 2020年2月13日
*/
List<JobDTO> querySysJobList(JobDTO jobDto) throws BizException;
/**
* @Description: 新增Quartz的定时任务
*
* @param com.XXX.attendance.entity.quartz.JobDTO
* @throws BizException
* @author oldw
* @date 2020年2月13日
*/
void insertJobInfo(JobDTO jobDto) throws BizException;
/**
* @Description: 修改Quartz的定时任务的状态:停用、启用
*
* @param com.XXX.attendance.entity.quartz.JobDTO
* @throws BizException
* @author oldw
* @date 2020年2月13日
*/
void updateJobStatus(JobDTO jobDto) throws BizException;
/**
* @Description: 修改Quartz的定时任务的表达式,描述
*
* @param com.XXX.attendance.entity.quartz.JobDTO
* @throws BizException
* @author oldw
* @date 2020年2月14日
*/
void updateJobInfo(JobDTO jobDto) throws BizException;
/**
* @Description: 删除Quartz的定时任务
*
* @param com.XXX.attendance.entity.quartz.JobDTO
* @throws BizException
* @author oldw
* @date 2020年2月14日
*/
void deleteJobinfo(JobDTO jobDto) throws BizException;
}
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.XXX.attendance.constant.Constant;
import com.XXX.attendance.entity.quartz.JobDTO;
import com.XXX.attendance.exception.BizException;
import com.XXX.attendance.mapper.primary.quartz.JobMapper;
import com.XXX.attendance.service.quartz.IJobService;
import com.XXX.attendance.util.SchedulerUtil;
@Service
public class JobServiceImpl implements IJobService {
@Autowired
private JobMapper jobMapper;
@Override
public List<JobDTO> querySysJobList(JobDTO jobDto) throws BizException {
return jobMapper.querySysJobList(jobDto);
}
@Override
public void insertJobInfo(JobDTO jobDto) throws BizException {
// 判断该新增定时任务是否存在
JobDTO resultJobDto = jobMapper.querysysJobIsExistByName(jobDto);
if (null != resultJobDto) {
throw new BizException("任务名为" + jobDto.getJobName() + "的任务已存在!");
}
// 新增定时任务
jobMapper.insertSysJobInfo(jobDto);
// quartz创建定时任务
try {
SchedulerUtil.addJob(jobDto.getJobClassPath(), jobDto.getJobName(), jobDto.getJobGroup(),
jobDto.getJobCron(), jobDto.getJobDataMap());
} catch (Exception e) {
throw new BizException("任务名为" + jobDto.getJobName() + "的任务创建失败!");
}
}
@Override
public void updateJobStatus(JobDTO jobDto) throws BizException {
// 判断该新增定时任务是否存在
JobDTO resultJobDto = jobMapper.querysysJobIsExistByName(jobDto);
if (null == resultJobDto) {
throw new BizException("任务ID为" + jobDto.getId() + "的任务不存在!");
}
JobDTO updateJobDto = new JobDTO();
updateJobDto.setId(jobDto.getId());
// 如果现在是启用,则停用
if (Constant.JOB_STATE.YES.equals(resultJobDto.getJobStatus())) {
updateJobDto.setJobStatus(Constant.JOB_STATE.NO);
// 检查job是否存在
try {
Boolean b = SchedulerUtil.isResume(resultJobDto.getJobName(), resultJobDto.getJobGroup());
// 存在则删除
if (b) {
SchedulerUtil.jobdelete(resultJobDto.getJobName(), resultJobDto.getJobGroup(), resultJobDto.getJobCron());
}
} catch (Exception e) {
throw new BizException("停用任务ID为" + jobDto.getId() + "的任务失败!");
}
}
// 如果现在是停用,则启用
if (Constant.JOB_STATE.NO.equals(resultJobDto.getJobStatus())) {
updateJobDto.setJobStatus(Constant.JOB_STATE.YES);
// 检查job是否存在
try {
Boolean b = SchedulerUtil.isResume(resultJobDto.getJobName(), resultJobDto.getJobGroup());
// 存在则激活,不存在则添加
if (b) {
SchedulerUtil.jobresume(resultJobDto.getJobName(), resultJobDto.getJobGroup());
} else {
SchedulerUtil.addJob(resultJobDto.getJobClassPath(), resultJobDto.getJobName(),
resultJobDto.getJobGroup(), resultJobDto.getJobCron(), resultJobDto.getJobDataMap());
}
} catch (Exception e) {
throw new BizException("启用任务ID为" + jobDto.getId() + "的任务失败!");
}
}
// 修改数据库中job状态信息
jobMapper.updateSysJobInfo(updateJobDto);
}
@Override
public void updateJobInfo(JobDTO jobDto) throws BizException {
// 判断该新增定时任务是否存在
JobDTO resultJobDto = jobMapper.querysysJobIsExistByName(jobDto);
if (null == resultJobDto) {
throw new BizException("任务ID为" + jobDto.getId() + "的任务不存在!");
}
if (StringUtils.isNotEmpty(jobDto.getJobCron()) && (jobDto.getJobCron().split(",").length != resultJobDto.getJobCron().split(",").length)) {
throw new BizException("原来定时任务的触发器个数与新增个数不一致,原来触发器个数为:" + resultJobDto.getJobCron().split(",").length);
}
// 修改数据库中job状态信息
jobMapper.updateSysJobInfo(jobDto);
// 只有任务状态为启用,才开始运行
// 如果先启动再手工插入数据,此处会报空指针异常
try {
if (StringUtils.isNotEmpty(jobDto.getJobCron()) && (Constant.JOB_STATE.YES.equals(resultJobDto.getJobStatus()))) {
SchedulerUtil.jobReschedule(resultJobDto.getJobName(), resultJobDto.getJobGroup(),
jobDto.getJobCron());
}
} catch (Exception e) {
throw new BizException("更新定时任务(ID为:" + jobDto.getId() + ")表达式失败!");
}
}
@Override
public void deleteJobinfo(JobDTO jobDto) throws BizException {
// 判断该新增定时任务是否存在
JobDTO resultJobDto = jobMapper.querysysJobIsExistByName(jobDto);
if (null == resultJobDto) {
throw new BizException("任务ID为" + jobDto.getId() + "的任务不存在!");
}
// 删除数据库中job状态信息
jobMapper.deteleSysJobInfo(jobDto);
try {
SchedulerUtil.jobdelete(resultJobDto.getJobName(), resultJobDto.getJobGroup(), resultJobDto.getJobCron());
} catch (Exception e) {
throw new BizException("删除定时任务(ID为:" + jobDto.getId() + ")失败!");
}
}
}
8、entity,提供两个JobDTO,MailDTO
/**
* <p>Description: 任务调度类<p>
* @author oldw
* @date 2020年2月13日
*/
@ApiModel("任务调度类")
public class JobDTO implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id; // 任务ID
private String jobName; // 任务名称
private String jobGroup; // 任务组别名
private String jobCron; // 任务表达式
private String jobClassPath; // 类路径
private String jobStatus; // 任务状态 1启用 0停用
private String jobDescribe; // 任务具体描述
private String jobDataMap;// 传递map参数
private Date createTime;// 创建时间
private Date updateTime;// 修改时间
。。。。
}
/**
* <p>Description: 发送邮件DTO<p>
* @author oldw
* @date 2020年2月24日
*/
public class MailDTO implements Serializable{
private static final long serialVersionUID = 1L;
// 发送者
private String sender;
// 接受者,如果是多人,使用数组的形式
private String receiver;
// 发送标题
private String subject;
// 发送内容
private String text;
。。。。
}
9、Job类集成BaseJob这个类,因为前面已经将MyFactory设置到调度器中,所以这里可以注入service接口
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.XXX.attendance.service.quartz.IJobService;
import com.XXX.attendance.util.BaseJob;
/**
1. <p>Description: 测试类1 <p>
2.
3. @author oldw
4. @date 2020年2月28日
*/
public class Test1 implements BaseJob {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private IJobService jobService;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
logger.info("测试任务执行了。。。。。请看下面");
JobDTO jobDto = new JobDTO();
jobDto.setJobStatus("1")
List<JobDTO> jobDtoList = jobService.querySysJobList(jobDto);
logger.info("成功将service注入到job中了" + jobDtoList.size());
}
}
10、注意及待优化点
1)、首先数据库att_quartz_job表中job_class_path存放的是com.XXX.attendance.task.Test1路径;
2)、job_data_map格式是{“data”:{“sender”:“发送者邮箱”,“receiver”:“接收者邮箱”,“subject”:“标题”,“text”:“内容”}}
3)、清楚quartz表的sql
DELETE FROM QRTZ_BLOB_TRIGGERS;
DELETE FROM QRTZ_CALENDARS;
DELETE FROM QRTZ_CRON_TRIGGERS;
DELETE FROM QRTZ_FIRED_TRIGGERS;
DELETE FROM QRTZ_LOCKS;
DELETE FROM QRTZ_PAUSED_TRIGGER_GRPS;
DELETE FROM QRTZ_SCHEDULER_STATE;
DELETE FROM QRTZ_SIMPLE_TRIGGERS;
DELETE FROM QRTZ_SIMPROP_TRIGGERS;
DELETE FROM QRTZ_TRIGGERS;
DELETE FROM QRTZ_JOB_DETAILS;
4)、本文没有考虑一个JOB对应多个触发器的情况,所以这种情况下需要创建多个JOB,一一对应。或者修改代码处理该情况。