最近在公司实习,发现公司有一套spring+Quartz的动态任务管理系统。可以使用Web界面进行任务动态的创建、删除、停止、运行和修改。刚好最近在学习spring boot。便使用spring boot2+quartz+thymeleaf+mysql数据库实现了一个简单的动态任务管理系统。
使用IDEA打开该项目,并把application.properties中数据信息修改为自己的数据信息即可。同时把sql文件夹下的sql文件导入到数据中即可。项目地址:http://localhost:8080/
项目地址Github地址:Quartz动态任务调度系统
quartz是一个java编写的开源任务调度框架其主要调度元素有:
对象之间的关系图如图下图所示:
添加quartz依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-quartzartifactId>
dependency>
添加spring boot2的web 、thymeleaf、依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
其他详情依赖请具体间github项目的pom文件。
相关属性的配置文件。
# 数据库连接的配置
spring.datasource.druid.url=jdbc:mysql://localhost:3306/dynamicquartz?useUnicode=true&characterEncoding=utf8
spring.datasource.druid.username=root
spring.datasource.druid.password=2012061128
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.druid.filters=stat
spring.datasource.druid.max-active=20
spring.datasource.druid.initialSize=1
spring.datasource.druid.max-wait=60000
spring.datasource.druid.min-idle=1
spring.datasource.druid.time-between-eviction-runs-millis=60000
spring.datasource.druid.min-evictable-idle-time-millis=300000
spring.datasource.druid.validation-query=select 'x'
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.test-on-return=false
spring.datasource.druid.test-on-borrow=false
# mybatis配置
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=cn.bupt.zcc.dynamicquartz.model
# quartz的一些属性配置
spring.quartz.job-store-type=jdbc
spring.quartz.properties.org.quartz.scheduler.instanceName=clusteredScheduler
spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO
spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
spring.quartz.properties.org.quartz.jobStore.tablePrefix=QRTZ_
#spring.quartz.properties.org.quartz.jobStore.isClustered=true
#spring.quartz.properties.org.quartz.jobStore.clusterCheckinInterval=10000
spring.quartz.properties.org.quartz.jobStore.useProperties=false
# 集成thymeleaf引擎的一些配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.check-template-location=true
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.cache=false
创建quartz任务工厂类:QuartzJobFactory。
/**
* @author: Zhang Chengcheng
* @create: 2018-05-31 14:38
**/
@Service("quartzJobFactory")
public class QuartzJobFactory extends QuartzJobBean {
@Autowired
QuartzService quartzService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
ScheduleJob object = (ScheduleJob) jobExecutionContext.getMergedJobDataMap().get("scheduleJob");
if(object.getMethodName()==null || object.getMethodName().equals("")){
quartzService.executeTask(object.getBeanName());
}else {
quartzService.executeTask(object.getBeanName(),object.getMethodName());
}
}
}
这是一个任务工厂类继承了QuartzJobBean类。每动态的添加一个定时任务就是创建此类的实例。并把要执行的定时任务类的名称或者全限定类名以及要执行的方法名传入。QuartzService获取传入的spring bean的名称或者全限定类名,获取该任务类的名称并使用反射来调用method,执行任务。通过反射的方式把要执行的任务类和Job接口或者QuartzJobBean类进行解耦。任务类无需实现Job接口或者集成QuartzJobBean类。即可实现任务的动态调度了。
QuartzService实现类如下:
/**
* @author: Zhang Chengcheng
* @create: 2018-05-31 15:34
**/
@Service("quartzService")
public class QuartzServiceImpl implements QuartzService {
private static final Logger logger = LoggerFactory.getLogger(QuartzServiceImpl.class);
private static final String METHODNAME = "execute";
@Override
public void executeTask(String beanName, String methodName) {
Object object = ApplicationContextUtil.getBean(beanName);
try {
logger.info("[QuartzServiceImpl] 反射调beanName:{},methodName:{}法开始.........",beanName,methodName);
if (beanName.contains("\\.")){
Class clazz = Class.forName(beanName);
Object cronJob = ApplicationContextUtil.getBean(clazz);
Method method1 = clazz.getMethod(methodName);
method1.invoke(cronJob);
}else {
Method method = object.getClass().getMethod(methodName);
method.invoke(object);
}
} catch (Exception e) {
logger.error("[QuartzServiceImpl] method invoke error,beanName:{},methodName:{}",beanName,methodName);
return;
}
logger.info("[QuartzServiceImpl] 反射调beanName:{},methodName:{}法结束.........",beanName,methodName);
}
ApplicationContextUtil是一个获取spring bean实例的工具类
/**
*
* @author: Zhang Chengcheng
* @create: 2018-05-31 15:07
**/
@Component("ApplicationContextUtil")
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtil.applicationContext = applicationContext;
}
public ApplicationContext getApplicationContext(){
checkApplicationContext();
return applicationContext;
}
public void checkApplicationContext(){
if (applicationContext==null){
throw new IllegalStateException("applicationContext 未注入,请在applicationContext.xml中定义SpringContextUtil");
}
}
/**
*根据bean的名称去寻找一个类的实例
* @param name
* @param
* @return
*/
public static T getBean(String name){
return (T) applicationContext.getBean(name);
}
}
ApplicationContextUtil类通过实现ApplicationContextAware接口,获取applicationContext上下文。从而在反射中获取任务的实例。再通过反射来执行任务。
动态添加任务
private void addJob(ScheduleJob scheduleJob) throws Exception{
checkNotNull(scheduleJob);
if (StringUtils.isBlank(scheduleJob.getCronExpression())){
throw new Exception("[SchedulerJobServiceImpl] CronExpression不能为空");
}
scheduleJob.setJobStatus("NORMAL");
int id = scheduleJobInService.insertSelective(scheduleJob);
logger.info("[SchedulerJobServiceImpl] the Primary key is:{}",scheduleJob.getId());
scheduleJob.setJobId(scheduleJob.getId()+"");
logger.info("[SchedulerJobServiceImpl] the scheduleJob is:{}",scheduleJob);
scheduleJobInService.updateByPrimaryKey(scheduleJob);
JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class).withIdentity(scheduleJob.getJobName(),scheduleJob.getJobGroup())
.build();//创建JobDetail,job是通过QuartzJobFactory.class工厂类反射创建的。
jobDetail.getJobDataMap().put("scheduleJob",scheduleJob);
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(scheduleJob.getJobName(),scheduleJob.getJobGroup())
.withSchedule(cronScheduleBuilder).build();//创建触发器
scheduler.scheduleJob(jobDetail,cronTrigger);//注册jobDetail和Trigger进行任务调度。
}
我们使用web的界面来实现任务的动态的添加。使用的是thymeleaf引擎。
参考:
https://github.com/jiwenxing/springboot-quartz “springboot-quartz”
https://www.jianshu.com/p/5995937f27bc “Quartz教程”
https://blog.csdn.net/u012907049/article/details/73801122 “Spring Boot集成持久化Quartz定时任务管理和界面展示”