一、引入依赖
org.springframework.boot
spring-boot-starter-quartz
com.alibaba
druid-spring-boot-starter
1.1.9
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
mysql
mysql-connector-java
5.1.47
org.projectlombok
lombok
true
log4j
log4j
1.2.17
com.baomidou
mybatis-plus-boot-starter
3.0.6
二、配置文件application.properties
## database
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://192.168.0.246:3306/hrz_task_center?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=1111
# Druid数据源配置
spring.datasource.driverClassName=com.mysql.jdbc.Driver
# 连接池配置
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.exceptionSorter=true
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.filters=stat,wall,log4j
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
spring.datasource.useGlobalDataSourceStat=true
# Druid监控器配置
#默认为stat,即开启sql监控。这里加了个wall,表示同时开启sql防火墙
spring.datasource.druid.filters=stat,wall,log4j
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*
spring.datasource.druid.web-stat-filter.session-stat-enable=true
spring.datasource.druid.web-stat-filter.session-stat-max-count=10
spring.datasource.druid.web-stat-filter.principal-session-name=
# Druid管理页配置
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.reset-enable=true
#监控页面登录用户名
spring.datasource.druid.stat-view-servlet.login-username=admin
#监控页面登录密码
spring.datasource.druid.stat-view-servlet.login-password=admin
# Druid AOP 配置
# Spring监控AOP切入点,如x.y.z.service.*,配置多个英文逗号分隔
spring.datasource.druid.aop-patterns=com.hrz.task.service.*
spring.aop.proxy-target-class=true
#连接数据库
#mybaties配置
#扫描实力类的所在包
mybatis-plus.type-aliases-package=com.hrz.task.entity
#通用mapper的所在接口名称 不只是包名
mybatis-plus.mapper-locations=classpath*:/mappers/**/*.xml
#在格式:logging.level.Mapper类的包=debug 会在控制台打印出sql语句
logging.level.com.hrz.third.mapper=debug
mybatis-plus.global-config.id-type=0
mybatis-plus.global-config.field-strategy=1
mybatis-plus.global-config.db-column-underline=true
mybatis-plus.global-config.refresh-mapper=true
mybatis-plus.global-config.db-config.table-underline=true
mybatis-plus.configuration.map-underscore-to-camel-case=true
mybatis-plus.configuration.cache-enabled=false
mybatis-plus.configuration.call-setters-on-nulls=true
mybatis-plus.global-config.db-config.table-prefix=task_
三、配置类ScheduleConfig
@Configuration
public class ScheduleConfig {
@Bean
public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
//配置数据源
factory.setDataSource(dataSource);
//quartz参数
Properties prop = new Properties();
prop.put("org.quartz.scheduler.instanceName", "HrzScheduler");
prop.put("org.quartz.scheduler.instanceId", "AUTO");
//线程池配置
prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "20");
prop.put("org.quartz.threadPool.threadPriority", "5");
//JobStore配置
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
//集群配置
prop.put("org.quartz.jobStore.isClustered", "true");
prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
prop.put("org.quartz.jobStore.misfireThreshold", "12000");
//前缀
prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
//PostgreSQL数据库,需要打开此注释
//prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
factory.setQuartzProperties(prop);
//调度名称
factory.setSchedulerName("HrzScheduler");
//延时启动
factory.setStartupDelay(30);
//可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
factory.setOverwriteExistingJobs(true);
//设置自动启动,默认为true
factory.setAutoStartup(true);
return factory;
}
}
四、工具类
1、ScheduleJob
@Slf4j
public class ScheduleJob extends QuartzJobBean {
private ExecutorService service = Executors.newSingleThreadExecutor();
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
JobKey key = context.getJobDetail().getKey();
log.info( "任务key:"+key);
String json = context.getMergedJobDataMap().getString(ScheduleJobs.JOB_PARAM_KEY);
//将获取的对象序列化的json 转化为实体类对象
ScheduleJobs scheduleJobs = JSON.parseObject(json, ScheduleJobs.class);
//获取spring bean
ScheduleJobLogService scheduleJobLogService = (ScheduleJobLogService) SpringUtils.getBean("scheduleJobLogService");
//数据库保存执行记录
ScheduleJobLog scheduleJobLog = new ScheduleJobLog();
scheduleJobLog.setJobId(scheduleJobs.getId());
scheduleJobLog.setBeanName(scheduleJobs.getBeanName());
scheduleJobLog.setMethodName(scheduleJobs.getMethodName());
scheduleJobLog.setParams(scheduleJobs.getParams());
scheduleJobLog.setCreateTime(new Date());
//任务开始时间
long startTime = System.currentTimeMillis();
try {
//执行任务
log.info("任务准备执行,任务ID:" + scheduleJobs.getId());
ScheduleRunnable task = new ScheduleRunnable(scheduleJobs.getBeanName(),
scheduleJobs.getMethodName(), scheduleJobs.getParams());
//接收多线程的执行结果
Future> future = service.submit(task);
future.get();
//任务执行总时长
long times = System.currentTimeMillis() - startTime;
scheduleJobLog.setTimes((int)times);
//任务状态 0:成功 1:失败
scheduleJobLog.setStatus(0);
log.info("任务执行完毕,任务ID:" + scheduleJobs.getId() + " 总共耗时:" + times + "毫秒");
} catch (Exception e) {
log.error("任务执行失败,任务ID:" + scheduleJobs.getId(), e);
//任务执行总时长
long times = System.currentTimeMillis() - startTime;
scheduleJobLog.setTimes((int)times);
//任务状态 0:成功 1:失败
scheduleJobLog.setStatus(1);
scheduleJobLog.setError(StringUtils.substring(e.toString(), 0, 2000));
}finally {
//插入日志
scheduleJobLogService.saveLog(scheduleJobLog);
}
}
}
2、ScheduleUtils
@Slf4j
public class ScheduleUtils {
private final static String JOB_NAME = "TASK_";
/**
* 获取触发器key
*/
public static TriggerKey getTriggerKey(String jobId) {
return TriggerKey.triggerKey(JOB_NAME + jobId);
}
/**
* 获取jobKey
*/
public static JobKey getJobKey(String jobId) {
return JobKey.jobKey(JOB_NAME + jobId);
}
/**
* 获取表达式触发器
*/
public static CronTrigger getCronTrigger(Scheduler scheduler, String jobId) throws HrzException {
try {
log.info("获取表达式触发器:" + jobId + ":" + scheduler);
return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
} catch (SchedulerException e) {
log.error("获取定时任务CronTrigger出现异常" + e);
throw new HrzException("获取定时任务CronTrigger出现异常", 500);
}
}
/**
* 创建定时任务
*/
public static void createScheduleJob(Scheduler scheduler, ScheduleJobs scheduleJobs) throws HrzException {
try {
//构建job信息
JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJobs.getId())).build();
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJobs.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
//按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJobs.getId())).withSchedule(scheduleBuilder).build();
//放入参数,运行时的方法可以获取
//将对象josn序列化存储到Job的getJobDataMap()方法中,为后续根据获取属性执行对应的类的任务
jobDetail.getJobDataMap().put(ScheduleJobs.JOB_PARAM_KEY, JSON.toJSONString(scheduleJobs));
scheduler.scheduleJob(jobDetail, trigger);
//暂停任务
if (scheduleJobs.getStatus() == GlobalConstant.ScheduleStatus.PAUSE.getValue()) {
pauseJob(scheduler, scheduleJobs.getId());
}
} catch (SchedulerException e) {
throw new HrzException("创建定时任务失败" + e, 500);
}
}
/**
* 更新定时任务
*/
public static void updateScheduleJob(Scheduler scheduler, ScheduleJobs scheduleJobs) throws HrzException {
try {
TriggerKey triggerKey = getTriggerKey(scheduleJobs.getId());
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJobs.getCronExpression())
.withMisfireHandlingInstructionDoNothing();
CronTrigger trigger = getCronTrigger(scheduler, scheduleJobs.getId());
//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
//将对象放入触发器中
trigger.getJobDataMap().put(ScheduleJobs.JOB_PARAM_KEY, JSON.toJSONString(scheduleJobs));
//接新的触发器重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
//暂停任务
if (scheduleJobs.getStatus() == GlobalConstant.ScheduleStatus.PAUSE.getValue()) {
pauseJob(scheduler, scheduleJobs.getId());
}
} catch (SchedulerException e) {
throw new HrzException("更新定时任务失败", 500);
}
}
/**
* 立即执行任务
*/
public static void run(Scheduler scheduler, ScheduleJobs scheduleJobs) throws HrzException {
try {
log.info("立即执行任务:" + scheduleJobs.getId());
//参数
JobDataMap dataMap = new JobDataMap();
dataMap.put(ScheduleJobs.JOB_PARAM_KEY, JSON.toJSONString(scheduleJobs));
scheduler.triggerJob(getJobKey(scheduleJobs.getId()), dataMap);
} catch (SchedulerException e) {
throw new HrzException("立即执行定时任务失败" + e, 500);
}
}
/**
* 暂停任务
*/
public static void pauseJob(Scheduler scheduler, String jobId) throws HrzException {
try {
scheduler.pauseJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new HrzException("暂停定时任务失败", 500);
}
}
/**
* 恢复任务
*/
public static void resumeJob(Scheduler scheduler, String jobId) throws HrzException {
try {
scheduler.resumeJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new HrzException("暂停定时任务失败", 500);
}
}
/**
* 删除定时任务
*/
public static void deleteScheduleJob(Scheduler scheduler, String jobId) throws HrzException {
try {
scheduler.deleteJob(getJobKey(jobId));
} catch (SchedulerException e) {
throw new HrzException("删除定时任务失败", 500);
}
}
}
3、
Slf4j
public class ScheduleRunnable implements Runnable {
private Object target;
private Method method;
private String params;
public ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException {
this.target = SpringUtils.getBean(beanName);
this.params = params;
if(StringUtils.isNotBlank(params)){
this.method = target.getClass().getDeclaredMethod(methodName, String.class);
}else{
this.method = target.getClass().getDeclaredMethod(methodName);
}
}
@Override
public void run() {
try {
ReflectionUtils.makeAccessible(method);
if(StringUtils.isNotBlank(params)){
method.invoke(target, params);
}else{
method.invoke(target);
}
}catch (Exception e) {
log.error("执行定时任务失败", 500);
}
}
}
3、ScheduleRunnable
@Slf4j
public class ScheduleRunnable implements Runnable {
private Object target;
private Method method;
private String params;
public ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException {
this.target = SpringUtils.getBean(beanName);
this.params = params;
if(StringUtils.isNotBlank(params)){
this.method = target.getClass().getDeclaredMethod(methodName, String.class);
}else{
this.method = target.getClass().getDeclaredMethod(methodName);
}
}
@Override
public void run() {
try {
ReflectionUtils.makeAccessible(method);
if(StringUtils.isNotBlank(params)){
method.invoke(target, params);
}else{
method.invoke(target);
}
}catch (Exception e) {
log.error("执行定时任务失败", 500);
}
}
}