Spring集成Quartz持久化+动态更改定时任务+Sql Server数据库+发送邮件

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,一一对应。或者修改代码处理该情况。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值