背景
Java 定时任务的实现技术主流的有三种, Java 自带的 Java.util.Time 类, Quartz,Spring3.0 以后自带的 task. 下面我们使用的是 Quartz.
项目中如果需要一个定时任务, 我们首先需要开发一个 job, 然后在 xml 中配置, 接着发布使用. 考虑以下几个问题:
如果发布后这个定时任务的时间需要改变一下怎么办
这个定时任务需要参数, 但是现在参数发生了改变怎么办
一个逻辑可以启动好几个定时任务, 只是参数和时间不一样, 那么我们是不是要在 xml 中复用同一个 job, 配置多个定时任务呢.
根据以上问题, 我们可以考虑实现定时器的时间和参数动态配置, 保存在数据库中, 项目启动时去数据库加载数据, 初始化定时任务. 当参数发生改变, 我们使用调度器改变任务参数. 这样我们就可以实现脚本的灵活复用, 脚本时间动态配置和定时任务参数的动态配置
以下配置基于 maven 和 spring mvc
步骤
1, 在 POM 文件中加入依赖
- <dependency>
- <groupId>org.quartz-scheduler</groupId>
- <artifactId>quartz</artifactId>
- <version>2.2.1</version>
- </dependency>
2, 配置 quartz.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
- <bean id="startQuertz" lazy-init="true" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
- </bean>
- <bean id="quartzManager" class="com.hello.quartz.QuartzManager" lazy-init="false" init-method="startJobs">
- <property name="scheduler" ref="startQuertz" />
- </bean>
- </beans>
3, 在 applicationContext.xml 加入 quartz 的依赖
<import resource="quartz.xml" />
4, 创建任务调度器 QuartzManager
- package com.cnc.ci.cs.quartz;
- 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.Trigger;
- import org.quartz.TriggerBuilder;
- import org.quartz.TriggerKey;
- public class QuartzManager {
- private Scheduler scheduler;
- public Scheduler getScheduler() {
- return scheduler;
- }
- public void setScheduler(Scheduler scheduler) {
- this.scheduler = scheduler;
- }
- /**
- * 功能: 添加一个定时任务
- * @param jobName 任务名, 可以是 JOB class 文件的首字母小写
- * @param jobGroupName 任务组名
- * @param triggerName 触发器名, 在 job 执行时 JobExecutionContext 参数中可以获取到值,((CronTriggerImpl) ((JobExecutionContextImpl) jobExecutionContext).getTrigger()).getName()
- * @param triggerGroupName 触发器组名
- * @param jobClass 任务的类类型 TestJob.class
- * @param cron 时间设置 表达式
- */
- public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron,
- Object... objects) {
- try {
- // 校验触发器是否已经存在, 如果存在, 不再创建. 注意, 每个触发器的 triggerName 应该不一样
- if (scheduler.checkExists(TriggerKey.triggerKey(triggerName, triggerGroupName))) {
- return;
- }
- // 任务名, 任务组, 任务执行类
- JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
- // 触发器
- if (objects != null) {
- for (int i = 0; i <objects.length; i++) {
- // 该数据可以通过 Job 中的 JobDataMap dataMap = context.getJobDetail().getJobDataMap(); 来进行参数传递值
- jobDetail.getJobDataMap().put("data" + (i + 1), objects[i]);
- }
- }
- TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
- // 触发器名, 触发器组
- triggerBuilder.withIdentity(triggerName, triggerGroupName);
- triggerBuilder.startNow();
- // 触发器时间设定
- triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
- // 创建 Trigger 对象
- CronTrigger trigger = (CronTrigger) triggerBuilder.build();
- // 调度容器设置 JobDetail 和 Trigger
- scheduler.scheduleJob(jobDetail, trigger);
- // 启动
- /*if (!scheduler.isShutdown()) {
- scheduler.start();
- }*/
- }
- catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- /**
- * 功能: 修改一个任务的触发时间
- * @param jobName
- * @param jobGroupName
- * @param triggerName 触发器名
- * @param triggerGroupName 触发器组名
- * @param cron 时间设置, 参考 quartz 说明文档
- */
- public void modifyJobTime(String jobName, String jobGroupName, String triggerName, String triggerGroupName, String cron) {
- try {
- TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
- CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
- if (trigger == null) {
- return;
- }
- String oldTime = trigger.getCronExpression();
- if (!oldTime.equalsIgnoreCase(cron)) {
- // 触发器
- TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
- // 触发器名, 触发器组
- triggerBuilder.withIdentity(triggerName, triggerGroupName);
- triggerBuilder.startNow();
- // 触发器时间设定
- triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
- // 创建 Trigger 对象
- trigger = (CronTrigger) triggerBuilder.build();
- // 方式一 : 修改一个任务的触发时间
- scheduler.rescheduleJob(triggerKey, trigger);
- }
- }
- catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- /**
- * 功能: 移除一个任务
- * @param jobName
- * @param jobGroupName
- * @param triggerName
- * @param triggerGroupName
- */
- public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {
- try {
- TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
- // 停止触发器
- scheduler.pauseTrigger(triggerKey);
- // 移除触发器
- scheduler.unscheduleJob(triggerKey);
- // 删除任务
- scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
- }
- catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- /**
- *
- * 功能: 启动所有定时任务
- */
- public void startJobs() {
- try {
- scheduler.start();
- }
- catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- /**
- * 功能: 关闭所有定时任务
- */
- public void shutdownJobs() {
- try {
- if (!scheduler.isShutdown()) {
- scheduler.shutdown();
- }
- }
- catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
5, 初始化定时任务
通过 ApplicationListener 接口监听 ContextRefreshedEvent 事件, 当所有的 bean 都初始化完成并被成功装载后会触发该事件, 实现 ApplicationListener<ContextRefreshedEvent > 接口可以收到监听动作, 并执行程序
- package com.cnc.ci.cs.quartz;
- import com.cnc.ci.cs.timerjob.TestJob;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.context.ApplicationListener;
- import org.springframework.context.event.ContextRefreshedEvent;
- import org.springframework.stereotype.Component;
- import com.cnc.portal.spring.SpringContextHolder;
- @Component
- public class ModJobManager implements ApplicationListener<ContextRefreshedEvent> {
- private static final Logger LOGGER = LoggerFactory.getLogger(ModJobManager.class);
- public void init() {
- LOGGER.info("初始化 job!");
- // 通过 spring 上下文获取 quartzManager,SpringContextHolder 类见下一个文件
- QuartzManager quartzManager = (QuartzManager) SpringContextHolder.getBean("quartzManager");
- quartzManager.addJob("testJob", "testJob", "testJob1", "testJob1", TestJob.class, "0 0/2 * * * ?");
- }
- @Override
- public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
- init();
- }
- }
- package com.hello.util;
- import org.springframework.beans.BeansException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
- import org.springframework.stereotype.Component;
- @Component
- public class SpringContextHolder implements ApplicationContextAware {
- // Spring 应用上下文环境
- public static ApplicationContext applicationContext;
- /**
- * 实现 ApplicationContextAware 接口的回调方法. 设置上下文环境
- *
- * @param applicationContext
- */
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) {
- SpringContextUtil.applicationContext = applicationContext;
- }
- /**
- * @return ApplicationContext
- */
- public static ApplicationContext getApplicationContext() {
- return applicationContext;
- }
- /**
- * 获取对象
- *
- * @param name
- * @return Object
- * @throws BeansException
- */
- public static Object getBean(String name) throws BeansException {
- return applicationContext.getBean(name);
- }
- }
6, 测试 JOB
- package com.cnc.ci.cs.timerjob;
- import org.quartz.Job;
- import org.quartz.JobExecutionContext;
- import org.quartz.JobExecutionException;
- import org.quartz.impl.JobExecutionContextImpl;
- import org.quartz.impl.triggers.CronTriggerImpl;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.stereotype.Component;
- @Component
- public class TestJob implements Job {
- private static final Logger LOGGER = LoggerFactory.getLogger(EmailSendJob.class);
- @Override
- public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
- // 获取 trigger 名字, trigger 名字 + group 是触发器的唯一标识
- LOGGER.info(((CronTriggerImpl) ((JobExecutionContextImpl) jobExecutionContext).getTrigger()).getName());
- // 获取触发器的时间
- LOGGER.info(((CronTriggerImpl) ((JobExecutionContextImpl) jobExecutionContext).getTrigger()).getCronExpression());
- // 业务逻辑
- }
- }
来源: http://www.bubuko.com/infodetail-2991158.html