前言
我们现在做的web项目有许多需求是需要用到定时任务的,quarze给我们带来了很多方便,如何使用quarze去帮助我们调度我们的任务呢?下面给大家分享一下:
项目目的:我们将需要执行的定时任务放进任务表中,应用定时扫描我们的任务,让quarze管理调度我们的任务。
以下代码亲测有效,,也可以直接复用,欢迎各位指正!
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>mlstudy</groupId>
<artifactId>mlstudy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging><properties>
<spring.version>4.3.12.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.29</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.4</version>
</dependency><!-- spring -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!-- <scope>test</scope> -->
</dependency>
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-context</artifactId>
<version> ${spring.version}</version>
</dependency>
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-core</artifactId>
<version> ${spring.version}</version>
</dependency>
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-beans</artifactId>
<version> ${spring.version}</version>
</dependency>
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-web</artifactId>
<version> ${spring.version}</version>
</dependency>
<dependency>
<groupId> org.springframework</groupId>
<artifactId> spring-webmvc </artifactId>
<version> ${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-support</artifactId>
<version>2.0.8</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
<!-- 数据库及映射 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency></dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
web.xml(别忘记让tomcat去加载我们的配置文件, ContextLoaderListener监听器必须要有哦)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>mlstudy</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/config/application-context.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
application-context.xml:
<beans xmlns= "http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd "><!-- 注册 AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor这四个bean -->
<context:annotation-config />
<!-- 自动扫描web包 ,将带有注解的类 纳入spring容器管理 。<context:component-scan/>不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能,同时还启用了注释驱动自动注入的功能(即还隐式地在内部注册了AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor),因此,一定程度上<context:annotation-config />可以注释掉 -->
<context:component-scan base-package="com.flower.job"></context:component-scan>
<!-- MVC注解支持 mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的,即解决了@Controller注解使用的前提配置-->
<mvc:annotation-driven/>
<context:property-placeholder location="classpath:/config/jdbc.properties"/>
<!-- 数据源配置 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="filters" value="stat"/>
<property name="maxActive" value="20"/>
<property name="initialSize" value="1"/>
<property name="maxWait" value="60000"/>
<property name="minIdle" value="1"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="validationQuery" value="SELECT 'x'"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnBorrow" value="false"/>
<property name="testOnReturn" value="false"/>
<property name="poolPreparedStatements" value="true"/>
<property name="maxOpenPreparedStatements" value="20"/>
</bean>
<!--事务就是对一系列的数据库操作进行统一的提交或回滚操作, Spring中也有自己的事务管理机制 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置mybatis核心 创建configurtion对象 SqlSessionFactoryBuilder根据传入的数据流生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactory实例-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:/config/mapperConfig.xml" />
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:/mapper/*.xml"></property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.flower.job.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- 配置quartze系统任务 -->
<bean id="quartzManagerBean" class="com.flower.job.QuarzeManage">
<property name="scheduler" ref="schedulerManager" />
</bean>
<bean id="quartzManagerJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="quartzManagerBean" />
<property name="targetMethod" value="execute" />
<property name="concurrent" value="false" />
</bean>
<!-- 主定时计划 每隔30秒钟触发一次,每30秒去扫描sys_job表 -->
<bean id="quartzManagerTrigger"
class=" org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="quartzManagerJobDetail" />
<property name="cronExpression" value="0/30 * * * * ?"/>
</bean>
<!-- 总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序 -->
<bean id="schedulerManager" lazy-init="false" autowire="no"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="quartzManagerTrigger" />
</list>
</property>
<property name="autoStartup" value="true"/>
</bean></beans>
jdbc.properties(配置数据库连接参数)
jdbc.url=jdbc:mysql://localhost:3306/flower? ?autoReconnect=true&zeroDateTimeBehavior=round&useUnicode=true&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=1234
ScheduleJob(实体类)
package com.flower.job.entity;
public class ScheduleJob {
int id;
String jobName;
String groupName;
String cron;
String className;
String targetClass;
String targetMehod;
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public String getCron() {
return cron;
}
public void setCron(String cron) {
this.cron = cron;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getTargetClass() {
return targetClass;
}
public void setTargetClass(String targetClass) {
this.targetClass = targetClass;
}
public String getTargetMehod() {
return targetMehod;
}
public void setTargetMehod(String targetMehod) {
this.targetMehod = targetMehod;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
mapperConfig.xml(mybatis配置文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--mybatis打印sql -->
<!-- <settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings> -->
<typeAliases>
<typeAlias alias="JSONObject" type="com.alibaba.fastjson.JSONObject" />
<typeAlias alias="JSONArray" type="com.alibaba.fastjson.JSONArray" />
<typeAlias alias="ScheduleJob" type="com.flower.job.entity.ScheduleJob" />
</typeAliases></configuration>
schedulerJob.xml(mapper文件)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.flower.job.dao.ScheduleDao" >
<resultMap id="BaseResultMap" type="ScheduleJob">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="job_name" jdbcType="VARCHAR" property="jobName" />
<result column="group_name" jdbcType="VARCHAR" property="groupName" />
<result column="cron" jdbcType="VARCHAR" property="cron" />
<result column="target_class" jdbcType="VARCHAR" property="targetClass" />
<result column="target_mehod" jdbcType="VARCHAR" property="targetMehod" />
</resultMap><select id="findAll" resultMap="BaseResultMap">
SELECT * FROM sys_job
</select></mapper>
ScheduleDao(查询dao)
package com.flower.job.dao;
import java.util.List;
import com.flower.job.entity.ScheduleJob;public interface ScheduleDao {
List<ScheduleJob> findAll();
}
QuarzeManage.java(核心调度类)
package com.flower.job;
import java.util.List;
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.TriggerBuilder;
import org.quartz.TriggerKey;
import org.springframework.beans.factory.annotation.Autowired;import com.flower.job.dao.ScheduleDao;
import com.flower.job.entity.ScheduleJob;
import com.flower.job.task.Task1;public class QuarzeManage {
@Autowired
private ScheduleDao scheduleDao;
//set注入scheduler
private Scheduler scheduler;
public Scheduler getScheduler() {
return scheduler;
}
public void setScheduler(Scheduler scheduler) {
this.scheduler = scheduler;
}
public void execute() {
List<ScheduleJob> list = scheduleDao.findAll();
for (ScheduleJob job : list){
createJob(job);
}
}
/**
* 添加任务
* @param job
*/
@SuppressWarnings({ "unchecked", "rawtypes"})
public void createJob(ScheduleJob job) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getGroupName());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
Class clazz= Class.forName(job.getTargetClass());
//不存在,则创建
if (null == trigger) {
JobDetail jobDetail = JobBuilder.
newJob(clazz).
withIdentity(job.getJobName(), job.getGroupName()).
build();CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron());
//withIdentity中写jobName和groupName
trigger = TriggerBuilder.
newTrigger().
withIdentity(job.getJobName(), job.getGroupName())
.withSchedule(scheduleBuilder)
.build();
scheduler.scheduleJob(jobDetail, trigger);
} else {
modifyJob(job,triggerKey);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 修改任务
* @param job
* @param triggerKey
*/
public void modifyJob(ScheduleJob job,TriggerKey triggerKey){
try {
if (null == triggerKey){
triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getGroupName());
}
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
if (!trigger.getCronExpression().equals(job.getCron())){
// Trigger已存在,那么更新相应的定时设置
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCron());
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);
}
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 移除任务
*/
public void removeJob(ScheduleJob job){
try {
TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getGroupName());
// 停止触发器
scheduler.pauseTrigger(triggerKey);
//移除触发器
scheduler.unscheduleJob(triggerKey);
// 删除任务
scheduler.deleteJob(JobKey.jobKey(job.getJobName(),job.getGroupName()));
} catch (SchedulerException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Task1 (子任务1)
package com.flower.job.task;
import java.text.SimpleDateFormat;
import java.util.Date;import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class Task1 implements Job{
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
System.out.println(df.format(new Date())+":子任务1");
}
}
Task2(子任务2)
package com.flower.job.task;
import java.text.SimpleDateFormat;
import java.util.Date;import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class Task2 implements Job{
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
System.out.println(df.format(new Date())+":子任务2");
}}
运行结果:
spring3.0+,Quarze 2.X搭配
spring3.0- Quarze 1.X搭配不然会出现:
使用Spring配置管理Quartz的时候会遇到下面的异常:
- Caused by: java.lang.IncompatibleClassChangeError: class org.springframework.scheduling.quartz.CronTriggerBean has interface org.quartz.CronTrigger as super class
原因是Spring 3.0版本中内置的Quartz版本是<2.0的,在使用最新的Quartz包(>2.0)之后,接口不兼容。
解决办法有三种:
1.降低Quartz版本,降到1.X去。
2.升级Spring版本到3.1+,根据Spring的建议,将原来的**TriggerBean替换成**TriggerFactoryBean,例如CronTriggerBean 就可以替换成 CronTriggerFactoryBean。替换之后问题解决。
3.如果不在xml配置文件中引用 Spring 3.0 是支持 Quartz2.2.1(目前最新版本),直接在程序中调用即可。
<!-- 配置quartze系统任务 -->
<bean id="quartzManagerBean" class="mldemo.lyl.quartze.QuartzeRun">
<property name="scheduler" ref="schedulerManager" />
</bean>
<bean id="quartzManagerJobDetail"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="quartzManagerBean" />
<property name="targetMethod" value="execute" />
<property name="concurrent" value="false" />
</bean>
<!-- 主定时计划 -->
<bean id="quartzManagerTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="quartzManagerJobDetail" />
<property name="cronExpression" value="*/5 * * * * ?"/><!-- 每隔5秒钟触发一次 -->
</bean>
<!-- 总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序 -->
<bean id="schedulerManager" lazy-init="false" autowire="no"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="quartzManagerTrigger" />
</list>
</property>
<property name="autoStartup" value="true"/>
</bean>