定时任务-quartz的使用,实现可页面化管理
使用spring+quartz实现定时任务的页面化管理。主要特点:
1.时间表达式等信息配置在数据库中,从而实现页面化管理。
2.可以手动执行或者停止单个任务,也可以使一个任务加入或者移出自动运行列表。
下面开始介绍用法,在这之前先说明框架的版本。spring3.2.4+quartz1.6.0
一.配置文件
只需要在spring的配置文件中加入:
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
</bean>
<bean id="jobManager" class="com.temobi.quartz.JobManager">
</bean>
说明:scheduler对象是spring管理定时任务的对象。
jobManager是我们自定义加载定时任务列表的对象,此对象加载所有任务列表后,将他们加入到自动运行列表。
二.jobManager代码:
public class JobManager implements InitializingBean {
private static final Log log = LogFactory.getLog(JobManager.class);
@Autowired
TaskJobService taskJobService;
@Autowired
QuartzManager quartzManager;
@Override
public void afterPropertiesSet() throws Exception {
loadAllJob();
}
private void loadAllJob(){
List<TaskJob> list =taskJobService.getTaskList();
quartzManager.enableCronSchedule(list);
}
}
说明:
1.实现spring中InitializingBean接口,并覆盖afterPropertiesSet方法,则该方法会在应用启动的时候而且其他servlet执行完成之后执行,此处用来加载所有定时任务,并加入定时任务自动运行列表。
2.quartzManager是我们自定义的定时任务管理类,用来实现我们文章开头说的功能。
三.QuartzManager代码
@Controller
public class QuartzManager {
@Autowired
Scheduler scheduler;
private static final Log log = LogFactory.getLog(QuartzManager.class);
public void enableCronSchedule(List<TaskJob> list) {
for(TaskJob task : list){
SchedulingJob job = new SchedulingJob();
job.setJobId(task.getId());
job.setJobName(task.getJobName());
//job.setMemos(task.getNote());
job.setCronExpression(task.getJobCronExpression());
try{
String className= task.getJobClass().trim();
Class clazz = Class.forName(className);
job.setStateFulljobExecuteClass(clazz);
}catch(Exception e){
e.printStackTrace();
continue;
}
JobDataMap paramsMap = new JobDataMap();
paramsMap.put("jobName", task.getJobName());
if(task.getParamsKey1()!=null && task.getParamsValue1()!=null){
paramsMap.put(task.getParamsKey1(), task.getParamsValue1());
}
if(task.getParamsKey2()!=null && task.getParamsValue2()!=null){
paramsMap.put(task.getParamsKey2(), task.getParamsValue2());
}
if(task.getParamsKey3()!=null && task.getParamsValue3()!=null){
paramsMap.put(task.getParamsKey3(), task.getParamsValue3());
}
enableCronSchedule(job, paramsMap, true);
log.info("系统结束初始化任务:"+task.getId()+":"+task.getJobName()+":"+task.getJobId());
}
}
/**
* 启动一个自定义的job
*
* @param schedulingJob
* 自定义的job
* @param paramsMap
* 传递给job执行的数据
* @param isStateFull
* 是否是一个同步定时任务,true:同步,false:异步
* @return 成功则返回true,否则返回false
*/
public boolean enableCronSchedule(SchedulingJob schedulingJob, JobDataMap paramsMap, boolean isStateFull) {
if (schedulingJob == null) {
return false;
}
try {
//scheduler = (Scheduler) ApplicationHelper.getBean("scheduler");
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(schedulingJob.getTriggerName(),
schedulingJob.getJobGroup());
if (null == trigger) {// 如果不存在该trigger则创建一个
JobDetail jobDetail = null;
if (isStateFull) {
jobDetail = new JobDetail(schedulingJob.getJobId(), schedulingJob.getJobGroup(),
schedulingJob.getStateFulljobExecuteClass());
} else {
jobDetail = new JobDetail(schedulingJob.getJobId(), schedulingJob.getJobGroup(),
schedulingJob.getJobExecuteClass());
}
jobDetail.setJobDataMap(paramsMap);
trigger = new CronTrigger(schedulingJob.getTriggerName(), schedulingJob.getJobGroup(),
schedulingJob.getCronExpression());
scheduler.scheduleJob(jobDetail, trigger);
} else {
// Trigger已存在,那么更新相应的定时设置
trigger.setCronExpression(schedulingJob.getCronExpression());
scheduler.rescheduleJob(trigger.getName(), trigger.getGroup(), trigger);
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 禁用一个job
*
* @param jobId
* 需要被禁用的job的ID
* @param jobGroupId
* 需要被警用的jobGroupId
* @return 成功则返回true,否则返回false
*/
public boolean disableSchedule(String jobId) {
if (jobId.equals("")) {
return false;
}
try {
String jobGroupId="DEFAULT";
Trigger trigger = getJobTrigger(jobId, jobGroupId);
if (null != trigger) {
scheduler.deleteJob(jobId, jobGroupId);
}
} catch (SchedulerException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 得到job的详细信息
*
* @param jobId
* job的ID
* @param jobGroupId
* job的组ID
* @return job的详细信息,如果job不存在则返回null
*/
public JobDetail getJobDetail(String jobId, String jobGroupId) {
if (jobId.equals("") || jobGroupId.equals("") || null == jobId || jobGroupId == null) {
return null;
}
try {
return scheduler.getJobDetail(jobId, jobGroupId);
} catch (SchedulerException e) {
e.printStackTrace();
return null;
}
}
/**
* 得到job对应的Trigger
*
* @param jobId
* job的ID
* @param jobGroupId
* job的组ID
* @return job的Trigger,如果Trigger不存在则返回null
*/
public Trigger getJobTrigger(String jobId, String jobGroupId) {
if (jobId.equals("") || jobGroupId.equals("") || null == jobId || jobGroupId == null) {
return null;
}
try {
return scheduler.getTrigger(jobId + "Trigger", jobGroupId);
} catch (SchedulerException e) {
e.printStackTrace();
return null;
}
}
}
说明:
1.主要方法有三个,启动一个任务,禁用一个任务,启动多个任务。启动即加入自动运行列表,禁用即移出自动运行列表。
2.TaskJob是一个任务对象,和数据库表结构相对应,后面给出数据库设计。
3.程序中有类似,
paramsMap.put(task.getParamsKey1(), task.getParamsValue1());
这样的代码,意思是假如你:如果你在数据库的ParamsKey1值为"username",ParamsValue1的值为"zhangsang".那么你在具体的job中给定变量名为"username"的变量并给出set/get方法,就可以得到值"zhangsang",此功能适用于给定时任务配置固定参数,并且参数名字随便你定。我们这里给了三个备用的,你也可以扩展,步骤就是数据库加一个字段,在上面的程序中paramsMap放入这个字段,当然你也可以不用参数。你数据库没有配置任何值,表示该定时任务没有固定参数。
4.SchedulingJob是一个定时任务执行参数的bean。即 将Taskjob对象的值经过处理转换成SchedulingJob对象,然后用SchedulingJob对象的值调用定时任务的API。
SchedulingJob对象主要做的事就是,TriggerName和JobGroup分别给出默认值。根据className生成StateFulljobExecuteClass的Class对象。
四。SchedulingJob代码:
public class SchedulingJob {
public static final int JS_ENABLED = 0; // 任务启用状态
public static final int JS_DISABLED = 1; // 任务禁用状态
public static final int JS_DELETE = 2; // 任务已删除状态
private String jobId; // 任务的Id,一般为所定义Bean的ID
private String jobName; // 任务的描述
private String jobGroup; // 任务所属组的名称
private int jobStatus; // 任务的状态,0:启用;1:禁用;2:已删除
private String cronExpression; // 定时任务运行时间表达式
private String memos; // 任务描述
private Class<?> stateFulljobExecuteClass;//同步的执行类,需要从StatefulMethodInvokingJob继承
private Class<?> jobExecuteClass;//异步的执行类,需要从MethodInvokingJob继承
/**
* 得到该job的Trigger名字
* @return
*/
public String getTriggerName() {
return this.getJobId() + "Trigger";
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getJobGroup() {
if(jobGroup==null){
jobGroup = Scheduler.DEFAULT_GROUP;
}
return jobGroup;
}
public void setJobGroup(String jobGroup) {
this.jobGroup = jobGroup;
}
public int getJobStatus() {
return jobStatus;
}
public void setJobStatus(int jobStatus) {
this.jobStatus = jobStatus;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public String getMemos() {
return memos;
}
public void setMemos(String memos) {
this.memos = memos;
}
public Class<?> getStateFulljobExecuteClass() {
return stateFulljobExecuteClass;
}
public void setStateFulljobExecuteClass(Class<?> stateFulljobExecuteClass) {
this.stateFulljobExecuteClass = stateFulljobExecuteClass;
}
public Class<?> getJobExecuteClass() {
return jobExecuteClass;
}
public void setJobExecuteClass(Class<?> jobExecuteClass) {
this.jobExecuteClass = jobExecuteClass;
}
public static int getJS_ENABLED() {
return JS_ENABLED;
}
public static int getJS_DISABLED() {
return JS_DISABLED;
}
public static int getJS_DELETE() {
return JS_DELETE;
}
}
五。具体JOB实现
只要继承QuartzJobBean类,覆盖executeInternal方法即可。在job中可能通过get方法的方式得到jobDetail对象中JobDataMap(详见QuartzManager类)中同名参数值。示例代码。
@Controller
public class ContentJob extends QuartzJobBean{
@Autowired
TaskJobService taskJobService;
private String beginDate;
private String endDate;
/**
* 手动执行任务
* @param request
*/
@RequestMapping("/contentJobManual.do")
public void manual(HttpServletRequest request) {
String startDate=request.getParameter("startDate");
String endDate=request.getParameter("endDate");
TaskJobService taskJobService = (TaskJobService) ApplicationHelper.getBean("taskJobService");
Map<String, String> param = new HashMap<String, String>();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String today=sdf.format(new Date());
if(StringUtils.isEmpty(startDate)){
param.put("beginDate", today);
}else{
param.put("beginDate", startDate);
}
if(StringUtils.isEmpty(endDate)){
param.put("endDate", today);
}else{
param.put("endDate", endDate);
}
taskJobService.callStatisticContent(param);
}
@Override
public void executeInternal(JobExecutionContext context) {
TaskJobService taskJobService = (TaskJobService) ApplicationHelper.getBean("taskJobService");
Map<String, String> param = new HashMap<String, String>();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String today=sdf.format(new Date());
if(StringUtils.isEmpty(beginDate)){
param.put("beginDate", today);
}else{
param.put("beginDate", beginDate);
}
if(StringUtils.isEmpty(endDate)){
param.put("endDate", today);
}else{
param.put("endDate", endDate);
}
taskJobService.callStatisticContent(param);
}
public String getBeginDate() {
return beginDate;
}
public void setBeginDate(String beginDate) {
this.beginDate = beginDate;
}
public String getEndDate() {
return endDate;
}
public void setEndDate(String endDate) {
this.endDate = endDate;
}
六。手动执行一个任务。可以将该job类声明成一个@Controller。另外写一个方法如上例中的manual方法。
扩展:目前还不能将自动运行的方法和手动执行的方法(即executeInternal方法和manual方法)写成一个。因为自动运行的方法不是一个action类,它不在web环境中,是通过反射实现的。如果把executeInternal这个方法强行配置成具有web功能的方法(即类上面加@Controller ,方法上面加@RequestMapping("/contentJobManual.do"))也是不行的,因为该方法没有HttpServletRequest对象,获取不了参数。除非你的定时任务没有参数。当然你也不能修改该方法的参数类型,因为他是覆盖QuartzJobBean的方法。
七。数据库设计。
ID VARCHAR2(60) N
JOB_CLASS VARCHAR2(255) N
JOB_NAME VARCHAR2(60) N
JOB_CRON_EXPRESSION VARCHAR2(60) N
JOB_SERVICE_BEAN VARCHAR2(60) Y
PARAMS_KEY1 VARCHAR2(60) Y
PARAMS_VALUE1 VARCHAR2(60) Y
PARAMS_KEY2 VARCHAR2(60) Y
PARAMS_VALUE2 VARCHAR2(60) Y
PARAMS_KEY3 VARCHAR2(60) Y
PARAMS_VALUE3 VARCHAR2(60) Y
NOTE VARCHAR2(255) Y
JOB_STATUS VARCHAR2(1) Y
UPDATETIME DATE Y
JOB_AUTORUN VARCHAR2(1) Y
JOB_GROUP VARCHAR2(60) Y
说明:JOB_STATUS表示是否有效任务,JOB_AUTORUN表示是否自动运行,JOB_SERVICE_BEAN表示手动执行的请求URL,JOB_CLASS表示JOB类的全路径,JOB_GROUP表示任务属于哪个组,方便对任务的分组管理(批量启动,禁止等),区别于quartz的API所要求的同名参数,其实也可以把这个值传给API。其它字段比较好理解。该表对应的bean是taskjob。
八。页面管理。
主要功能是将一个任务加入或者移出自动运行队列(通过quartzManager对象)。和任务的增删查改。示例代码如下:
@Controller
public class TaskJobAction{
private static final Log log = LogFactory.getLog(TaskJobAction.class);
@Autowired
TaskJobService taskJobService;
@Autowired
QuartzManager quartzManager;
@RequestMapping("/enableTask.do")
public void enableTask(HttpServletRequest request, HttpServletResponse response) throws IOException {
String parameterStr="";
parameterStr = IOUtils.toString(request.getInputStream(), InputConstant.CHAR_SET);
parameterStr = StringUtils.trim(parameterStr);
parameterStr =URLDecoder.decode(parameterStr,"utf-8");
Map<String, String> p = JsonUtil.getParameterMap(parameterStr);
String id=p.get("id");
if(!StringUtils.isEmpty(id)){
TaskJob task=taskJobService.getTaskById(id);
List<TaskJob> list=new ArrayList<TaskJob>();
list.add(task);
quartzManager.enableCronSchedule(list);
task.setJobEnabled("Y");
taskJobService.update(task);//将任务设置成自动运行状态
}
}
@RequestMapping("/disableTask.do")
public void disableTask(HttpServletRequest request, HttpServletResponse response) throws IOException {
String parameterStr="";
parameterStr = IOUtils.toString(request.getInputStream(), InputConstant.CHAR_SET);
parameterStr = StringUtils.trim(parameterStr);
parameterStr =URLDecoder.decode(parameterStr,"utf-8");
Map<String, String> p = JsonUtil.getParameterMap(parameterStr);
String id=p.get("id");
if(!StringUtils.isEmpty(id)){
TaskJob task=taskJobService.getTaskById(id);
quartzManager.disableSchedule(task.getJobId());
task.setJobEnabled("N");
taskJobService.update(task);//将任务设置成非运行状态
}
}
@RequestMapping("/add.do")
public void add(HttpServletRequest request, HttpServletResponse response) throws IOException {
String parameterStr="";
parameterStr = IOUtils.toString(request.getInputStream(), InputConstant.CHAR_SET);
parameterStr = StringUtils.trim(parameterStr);
Map<String, String> p = JsonUtil.getParameterMap(parameterStr);
String jobjson=p.get("jobjson");
jobjson=URLDecoder.decode(jobjson,"utf-8");
TaskJob task=JsonUtil.toObject(jobjson, TaskJob.class);
String jobName=URLDecoder.decode(task.getJobName(),"utf-8");
task.setJobName(jobName);
if(!StringUtils.isEmpty(jobjson)){
taskJobService.insert(task);
}
}
@RequestMapping("/update.do")
public void update(HttpServletRequest request, HttpServletResponse response) throws IOException {
String parameterStr="";
parameterStr = IOUtils.toString(request.getInputStream(), InputConstant.CHAR_SET);
parameterStr = StringUtils.trim(parameterStr);
Map<String, String> p = JsonUtil.getParameterMap(parameterStr);
String jobjson=p.get("jobjson");
jobjson=URLDecoder.decode(jobjson,"utf-8");
TaskJob task=JsonUtil.toObject(jobjson, TaskJob.class);
String jobName=URLDecoder.decode(task.getJobName(),"utf-8");
task.setJobName(jobName);
if(!StringUtils.isEmpty(jobjson)){
taskJobService.update(task);
}
}
@RequestMapping("/delete.do")
public void delete(HttpServletRequest request, HttpServletResponse response) throws IOException {
String parameterStr="";
parameterStr = IOUtils.toString(request.getInputStream(), InputConstant.CHAR_SET);
parameterStr = StringUtils.trim(parameterStr);
parameterStr =URLDecoder.decode(parameterStr,"utf-8");
Map<String, String> p = JsonUtil.getParameterMap(parameterStr);
String idStr=p.get("idStr");
if(!StringUtils.isEmpty(idStr)){
taskJobService.delete(idStr);
}
}
@ResponseBody
@RequestMapping("/taskList.do")
public RecordResultBean list(HttpServletRequest request, HttpServletResponse response,@RequestParam("pageSize") int pageSize,
@RequestParam("startIndex") int startIndex) throws IOException {
int pageNum=startIndex/pageSize+1;
String startDate=request.getParameter("startDate");
String endDate=request.getParameter("endDate");
String jobName=request.getParameter("jobName");
if (!StringUtils.isEmpty(jobName)) {
jobName=URLDecoder.decode(jobName, "UTF-8");
jobName=URLDecoder.decode(jobName, "UTF-8");
}
RecordResultBean resultBean = new RecordResultBean();
try {
Pager pager = new Pager(pageNum, pageSize);
Map<String, Object> map = new HashMap<String, Object>();
map.put("jobName",jobName);
pager.setKeys(map);
pager = taskJobService.findPage(pager);
if (pager == null || pager.getTotalCount() == 0) {
resultBean.setResult(false);
} else {
resultBean.setResult(true);
resultBean.setBean(pager);
}
}catch(Exception ex) {
log.warn(JDKStackTrace.getJDKStrack(ex));
}
return resultBean;
}