您的位置:首页 > 编程语言 > Java开发

[整理]在Spring MVC中使用Quartz实现定时任务动态管理

2017-12-12 09:35 585 查看
项目应用中有许多定时任务,当需要修改定时器时,往往需要停服务,这不是我们想要的。

于是动态管理项目中的定时任务就成了要解决的问题。

项目原来的定时任务是直接使用spring自己的scheduled-tasks实现的,因为是无状态的,没法满足我们的需求。

需要改造原来的定时任务,实现StatefulMethodInvokingJob类来解决。

大概的思路是把定时任务的参数数据保存到数据库,应用启动的时候从数据库读取配置它们,通过页面来实时启动和关闭它们。

项目比较老,使用的quartz-all-1.6.jar,spring用的是4.0.5

1、首先定义entity和数据库表,实现数据库的CRUD,这里列出entity其它的很简单就不列出来了

public class QuartzJobModel {
private String jobId;
private String jobName;
private String targetClassName;
private String cronExpression;
private String jobStatus;
private String runOnHoliday;
private String jobDesc;
private Date createTime;
private String createrName;
private Date updateTime;
private String updaterName;
// getter &setter
}


2、改造定时任务,这里我们新建两个定时任务,一个打印三角形,一个打印菱形

public class PrintRhombusJob extends StatefulMethodInvokingJob {
private static final Logger logger = Logger.getLogger(PrintRhombusJob.class);

/**
* @see org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean.MethodInvokingJob#executeInternal(org.quartz.JobExecutionContext)
*/

@Override
protected void executeInternal(JobExecutionContext cts) throws JobExecutionException {
logger.info("================== print rhombus job begin =================");
System.out.println("print rhombus");
logger.info("================== print rhombus job end =================");
}

}

public class PrintTraingleJob extends StatefulMethodInvokingJob {
private static final Logger logger = Logger.getLogger(PrintTraingleJob.class);

/**
* @see org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean.MethodInvokingJob#executeInternal(org.quartz.JobExecutionContext)
*/

@Override
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
logger.info("================== print traingle job begin =================");
System.out.println("print traingle");
logger.info("================== print traingle job end =================");
}

}


3、编写定时任务管理器,实现对定时任务的开关

@Component("quartzJobManager")
public class QuartzJobManager {
private static final Logger logger = Logger.getLogger(QuartzJobManager.class);
@Autowired
private SchedulerFactoryBean schedulerFactoryBean;
@Autowired
private QuartzJobDAO quartzJobDAO;
/** 默认任务组名称 */
private static final String DEFAULT_JOB_GROUP_NAME = "DefaultJobGroup";
/** 默认触发器组名称 */
private static final String DEFAULT_TRIGGER_GROUP_NAME = "DefaultTriggerGroup";

/**
* 加载数据库中已定义的定时任务
*
* @Title: loadJobs
* @Description: 加载数据库中已定义的定时任务
*/
public void loadJobs() {
List<QuartzJobModel> jobs = quartzJobDAO.getAll();
if (null != jobs && !jobs.isEmpty()) {
for (QuartzJobModel job : jobs) {
addJob(job);
}
}
}

/**
* 重新加载数据库中已定义的定时任务
*
* @Title: reloadJobs
* @Description: 重新加载数据库中已定义的定时任务
*/
public void reloadJobs() {
removeAll();
loadJobs();
}

/**
* 添加一个新的定时任务
*
* @Title: addJob
* @Description: 添加一个新的定时任务
* @param job QuartzJobModel
*/
public void addJob(QuartzJobModel job) {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
try {
// 使用"jobName+默认任务组名称"作为定时任务的Key
JobDetail jobDetail = new JobDetail(job.getJobName(), DEFAULT_JOB_GROUP_NAME,
Class.forName(job.getTargetClassName()));
// 使用"jobName+默认触发器组名称"作为定时任务触发器的Key
CronTrigger trigger = new CronTrigger(job.getJobName(), DEFAULT_TRIGGER_GROUP_NAME,
job.getCronExpression());
scheduler.scheduleJob(jobDetail, trigger);
logger.info("******注册定时任务:" + job + " ******");
if (!scheduler.isShutdown()) {
scheduler.start();
}
} catch (ClassNotFoundException e) {
logger.error("注册定时任务失败:" + job + e.getMessage());
e.printStackTrace();
} catch (ParseException e) {
logger.error("注册定时任务失败:" + job + e.getMessage());
e.printStackTrace();
} catch (SchedulerException e) {
logger.error("注册定时任务失败:" + job + e.getMessage());
e.printStackTrace();
}

}

/**
* 修改定时任务
*
* @Title: modifyJob
* @Description: 修改定时任务
* @param job QuartzJobModel
*/
public void modifyJob(QuartzJobModel job) {
removeJob(job);
addJob(job);
}

/**
* 删除定时任务
*
* @Title: removeJob
* @Description: 删除定时任务
* @param job QuartzJobModel
*/
public void removeJob(QuartzJobModel job) {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
try {
// 根据""暂停触发器
scheduler.pauseTrigger(job.getJobName(), DEFAULT_TRIGGER_GROUP_NAME);
// 根据""移除触发器
scheduler.unscheduleJob(job.getJobName(), DEFAULT_TRIGGER_GROUP_NAME);
// 根据""删除定时任务
scheduler.deleteJob(job.getJobName(), DEFAULT_JOB_GROUP_NAME);
logger.info("******删除定时任务:" + job + " ******");
} catch (SchedulerException e) {
logger.error("移除定时任务失败:" + job + e.getMessage());
e.printStackTrace();
}
}

/**
* 移除所有定时任务
*
* @Title: removeAll
* @Description: 移除所有定时任务
*/
public void removeAll() {
List<QuartzJobModel> jobs = quartzJobDAO.getAll();
if (null != jobs && !jobs.isEmpty()) {
for (QuartzJobModel job : jobs) {
removeJob(job);
}
}
}

/**
* 暂停定时任务
*
* @Title: pauseJob
* @Description: 暂停定时任务
* @param job QuartzJobModel
*/
public void pauseJob(QuartzJobModel job) {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
try {
scheduler.pauseJob(job.getJobName(), DEFAULT_JOB_GROUP_NAME);
logger.info("******暂停定时任务:" + job + " ******");
} catch (SchedulerException e) {
logger.error("暂停定时任务失败:" + job + e.getMessage());
e.printStackTrace();
}
}

/**
* 恢复定时任务
*
* @Title: resumeJob
* @Description: 恢复定时任务
* @param job QuartzJobModel
*/
public void resumeJob(QuartzJobModel job) {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
try {
scheduler.resumeJob(job.getJobName(), DEFAULT_JOB_GROUP_NAME);
logger.info("******恢复定时任务:" + job + " ******");
} catch (SchedulerException e) {
logger.error("恢复定时任务失败:" + job + e.getMessage());
e.printStackTrace();
}
}

/**
* 立刻执行一次任务
*
* @Title: triggerJob
* @Description: 立刻执行一次任务
* @param job QuartzJobModel
*/
public void triggerJob(QuartzJobModel job) {
Scheduler scheduler = schedulerFactoryBean.getScheduler();
try {
scheduler.triggerJob(job.getJobName(), DEFAULT_JOB_GROUP_NAME);
logger.info("******立刻执行定时任务:" + job + " ******");
} catch (SchedulerException e) {
logger.error(" 立刻执行定时任务失败:" + job + e.getMessage());
e.printStackTrace();
}
}
}


4、编写Contoller和页面,实现Web化管理,这里页面就不贴了

@Controller
@RequestMapping(value = "/jobs")
public class JobController extends BaseController {
private static Logger logger = Logger.getLogger(JobController.class);

@Autowired
private QuartzJobService quartzJobService;
@Autowired
private QuartzJobManager quartzJobManager;

@RequestMapping(value = "/list")
public String list() {
return "list";
}

@RequestMapping(value = "/queryListByPage")
@RunningControllerLog(description = "分页查询")
public void queryListByPage(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> params = HttpServletUtils
.parseReqToSearchCondition(new String[] { "jobStatus", "runOnHoliday", "jobName" }, request);
// 权限条件追加
PermUtils.appendPermParams(params);

PageModel<QuartzJobModel> pageModel = new PageModel<>(request);
pageModel.setSearchCdtns(params);
try {
pageModel = quartzJobService.findListByPage(pageModel);
returnJSONData(response, pageModel.toJSONString());
} catch (Exception e) {
logger.error("分页查询异常,异常信息:" + e);
}
}

@RequestMapping(value = "/toAdd")
public String toAdd() {
return "add";
}

@RequestMapping(value = "/add",
method = RequestMethod.POST)
public void add(HttpServletRequest request, HttpServletResponse response) {
String jobName = request.getParameter("jobName");
String targetClassName = request.getParameter("targetClassName");
String cronExpression = request.getParameter("cronExpression");
String runOnHoliday = request.getParameter("runOnHoliday");
String jobDesc = request.getParameter("jobDesc");

Date date = new Date();
SysUser user = getSysUser(request);
String userName = user.getUserName();

QuartzJobModel job = new QuartzJobModel();
job.setJobName(jobName);
job.setTargetClassName(targetClassName);
job.setCronExpression(cronExpression);
if (!StringUtil.isEmpty(jobDesc)) {
job.setJobDesc(jobDesc);
} else {
job.setJobDesc("");
}
job.setRunOnHoliday(runOnHoliday);
job.setJobStatus(JobStatusEnum.RUNNING.getValue());
job.setCreaterName(userName);
job.setCreateTime(date);
job.setUpdaterName(userName);
job.setUpdateTime(date);

quartzJobManager.addJob(job);
logger.info("^^^^^^^(add " + job + " by " + getSysUser(request).getUserName() + ")^^^^^^");
int result = quartzJobService.save(job);
returnJSONData(response, JSON.toJSONString(result));
}

@RequestMapping(value = "/toEdit/{jobId}")
public String toEdit(HttpServletRequest request, HttpServletResponse response,
@PathVariable("jobId") String jobId) {
QuartzJobModel job = quartzJobService.getById(jobId);
request.setAttribute("item", job);
return "edit";
}

@RequestMapping(value = "/edit",
method = RequestMethod.POST)
public void edit(HttpServletRequest request, HttpServletResponse response) {
String jobId = request.getParameter("jobId");
String cronExpression = request.getParameter("cronExpression");
String runOnHoliday = request.getParameter("runOnHoliday");
String jobDesc = request.getParameter("jobDesc");

Date date = new Date();
SysUser user = getSysUser(request);
String userName = user.getUserName();

QuartzJobModel job = quartzJobService.getById(jobId);

job.setCronExpression(cronExpression);
if (!StringUtil.isEmpty(jobDesc)) {
job.setJobDesc(jobDesc);
} else {
job.setJobDesc("");
}
job.setRunOnHoliday(runOnHoliday);
job.setUpdaterName(userName);
job.setUpdateTime(date);
int result = quartzJobService.update(job);
job = quartzJobService.getById(jobId);
quartzJobManager.modifyJob(job);
logger.info("^^^^^^^(edit " + job + " by " + getSysUser(request).getUserName() + ")^^^^^^");

returnJSONData(response, JSON.toJSONString(result));
}

@RequestMapping(value = "/pause/{jobId}",
method = RequestMethod.POST)
public void pause(HttpServletRequest request, HttpServletResponse response, @PathVariable("jobId") String jobId) {
Date date = new Date();
SysUser user = getSysUser(request);
String userName = user.getUserName();

QuartzJobModel job = quartzJobService.getById(jobId);

job.setJobStatus(JobStatusEnum.PAUSED.getValue());
job.setUpdaterName(userName);
job.setUpdateTime(date);

quartzJobManager.pauseJob(job);
logger.info("^^^^^^^(pause " + job + " by " + getSysUser(request).getUserName() + ")^^^^^^");
int result = quartzJobService.update(job);
returnJSONData(response, JSON.toJSONString(result));
}

@RequestMapping(value = "/resume/{jobId}",
method = RequestMethod.POST)
public void resume(HttpServletRequest request, HttpServletResponse response, @PathVariable("jobId") String jobId) {
Date date = new Date();
SysUser user = getSysUser(request);
String userName = user.getUserName();

QuartzJobModel job = quartzJobService.getById(jobId);
if (job.getJobStatus().equals(JobStatusEnum.PAUSED.getValue())) {
quartzJobManager.resumeJob(job);
} else if (job.getJobStatus().equals(JobStatusEnum.STOPPED.getValue())) {
quartzJobManager.addJob(job);
}
logger.info("^^^^^^^(sesume " + job + " by " + getSysUser(request).getUserName() + ")^^^^^^");
job.setJobStatus(JobStatusEnum.RUNNING.getValue());
job.setUpdaterName(userName);
job.setUpdateTime(date);
int result = quartzJobService.update(job);
returnJSONData(response, JSON.toJSONString(result));
}

@RequestMapping(value = "/run/{jobId}",
method = RequestMethod.POST)
public void runJob(HttpServletRequest request, HttpServletResponse response, @PathVariable("jobId") String jobId) {
QuartzJobModel job = quartzJobService.getById(jobId);
logger.info("^^^^^^^(run " + job + " by " + getSysUser(request).getUserName() + ")^^^^^^");
quartzJobManager.triggerJob(job);
}

@RequestMapping(value = "/reloadJobs",
method = RequestMethod.POST)
public void reloadJobs(HttpServletRequest request, HttpServletResponse response) {
logger.info("^^^^^^^(reload jobs by " + getSysUser(request).getUserName() + ")^^^^^^");
quartzJobManager.reloadJobs();
}

@RequestMapping(value = "/remove/{jobId}",
method = RequestMethod.POST)
public void remove(HttpServletRequest request, HttpServletResponse response, @PathVariable("jobId") String jobId) {
QuartzJobModel job = quartzJobService.getById(jobId);
logger.info("^^^^^^^(remove job " + job + " by " + getSysUser(request).getUserName() + ")^^^^^^");
quartzJobManager.removeJob(job);
job.setJobStatus(JobStatusEnum.STOPPED.getValue());
int result = quartzJobService.update(job);
returnJSONData(response, JSON.toJSONString(result));
}

@RequestMapping(value = "/getJobStatus",
method = RequestMethod.POST)
public void getJobStatus(HttpServletRequest request, HttpServletResponse response) {
StringBuilder options = new StringBuilder();
List<JobStatusEnum> list = JobStatusEnum.getAll();
options.append("<option value=''>--请选择--</option>");
for (JobStatusEnum item : list) {
options.append("<option value='" + item.getValue() + "'>" + item.getName() + "</option>");
}
String json = "{\"html\":\"" + options.toString() + "\"}";
returnJSONData(response, json);
}
}


5、还差一点就是应用启动时自动加载数据库内的定时任务,这里需要实现一个ApplicationListener
@Component("StartupListener")
public class InitSystemJobsListener implements ApplicationListener<ContextRefreshedEvent> {
private int runTime = 0;

/**
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
runTime++;
if (2 == runTime) {
// 获取spring管理的Bean
ApplicationContext context = event.getApplicationContext();
QuartzJobManager quartzJobManager = (QuartzJobManager) context.getBean("quartzJobManager");
quartzJobManager.loadJobs();
}
}

}


好了,主要工作完成了,我们启动应用试一试





页面如下:



试验一下暂停:



暂停以后,控制台没有输出,恢复一下



其它的就不贴图了。就这样吧。

实际应用中发现一个缺陷是,定时任务内部使用Spring托管的bean进行依赖注入时会报空指针异常,这里需要改造SchedulerFactoryBean 。

@Component
public class MyJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;

/**
* @see org.springframework.scheduling.quartz.AdaptableJobFactory#createJobInstance(org.quartz.spi.TriggerFiredBundle)
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
Object jobInstance = super.createJobInstance(bundle);
// 实现Job的IOC管理
autowireCapableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}

@Configuration
@EnableScheduling
public class QuartzJobConfig {
@Autowired
private MyJobFactory myJobFactory;

@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setOverwriteExistingJobs(true);
factory.setStartupDelay(20);
factory.setJobFactory(myJobFactory);
return factory;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐