Quartz学习笔记-Spring整合与简单使用

Quartz 是个开源的作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。Quartz 允许开发人员根据时间间隔来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联。整合了 Quartz 的应用程序可以重用来自不同事件的作业,还可以为一个事件组合多个作业。这里主要记录一下Spring与Quartz的整合集成使用。


quartz.properties

#============================================================================
# Configure Scheduler
#============================================================================
# 当多个调度器实例在一个程序里时,就需要为客户端代码区别每个调度器。
# 如果使用集群特性,必须为在集群里的每个实例用一样的名字,实现逻辑上一样的调度器。
org.quartz.scheduler.instanceName = QuartzScheduler   
# 如果在一个集群里多个实例是一个逻辑上一样的调度器时,每个实例的这项属性必须唯一。
# 可以设置这项为“AUTO”从而自动收集ID。
org.quartz.scheduler.instanceId = AUTO  
# 这个属性设置Scheduler在检测到JobStore到某处的连接(比如到数据库的连接)断开后,
# 再次尝试连接所等待的毫秒数。这个参数在使用RamJobStore无效。
org.quartz.scheduler.dbFailureRetryInterval = 1500
# 如果想要连接到远程的调度器服务,要设置为true,指定一个主机和端口号。
org.quartz.scheduler.rmi.proxy = false

#============================================================================
# Configure ThreadPool
#============================================================================
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

#============================================================================
# Configure JobStore
#============================================================================
# 将Scheduler相关信息保存在RDB中.有两种实现:JobStoreTX和JobStoreCMT
# 前者为Application自己管理事务,后者为Application Server管理事务,即全局事务JTA
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
# 类似于Hibernate的Dialect,用于处理DB之间的差异,StdJDBCDelegate能满足大部分的DB
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 设置数据源,数据源将在应用服务器里被配置和管理
org.quartz.jobStore.dataSource = defaultDS 
# 设置属性为true是让Quartz不去在JDBC连接上调用setAutoCommit(false)这个函数
org.quartz.jobStore.dontSetAutoCommitFalse = false
# 在触发器被认为没有触发之前,调度器能承受一个触发器再次触发的一个毫秒级数字。
# 最大能忍受的触发超时时间,如果超过则认为“失误”
org.quartz.jobStore.misfireThreshold = 60000
# 数据库中表名前缀
org.quartz.jobStore.tablePrefix = QRTZ_
# JobStore处理未按时触发的Job的数量
org.quartz.jobStore.maxMisfiresToHandleAtATime = 10
# JobDataMaps是否都为String类型
org.quartz.jobStore.useProperties = true
# 是否是应用在集群中,当应用在集群中时必须设置为TRUE,否则会出错
org.quartz.jobStore.isClustered = true
# 集群检测间隔,Scheduler的Checkin时间,时间长短影响Failure Scheduler的发现速度
org.quartz.jobStore.clusterCheckinInterval = 20000

#============================================================================
# Configure DataSource
#============================================================================
org.quartz.dataSource.defaultDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.defaultDS.URL = jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
org.quartz.dataSource.defaultDS.user = root
org.quartz.dataSource.defaultDS.password = 123456
org.quartz.dataSource.defaultDS.maxConnections = 10
org.quartz.dataSource.defaultDS.validationQuery = select 1
org.quartz.dataSource.defaultDS.idleConnectionValidationSeconds = 50
org.quartz.dataSource.defaultDS.validateOnCheckout = false
org.quartz.dataSource.defaultDS.discardIdleConnectionsSeconds = 1000

#============================================================================
# Configure PlugIn
#============================================================================
# Trigger历史日志记录插件
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingTriggerHistoryPlugin
org.quartz.plugin.triggHistory.triggerFiredMessage = Trigger \{1\}.\{0\} fired job \{6\}.\{5\} at: \{4, date, HH:mm:ss MM/dd/yyyy}
org.quartz.plugin.triggHistory.triggerCompleteMessage = Trigger \{1\}.\{0\} completed firing job \{6\}.\{5\} at \{4, date, HH:mm:ss MM/dd/yyyy\}.
# Shutdown Hook插件,通过捕捉JVM关闭时的事件,来关闭调度器
org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin
org.quartz.plugin.shutdownhook.cleanShutdown = true


applicationContext-scheduler.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" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans classpath:/org/springframework/beans/factory/xml/spring-beans-3.1.xsd
						http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
						http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
						http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd" default-autowire="byName">
	
	<!-- 注解配置  -->
	<context:component-scan base-package="org.platform">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>  
	</context:component-scan>
	
	<!-- 数据库加载文件配置 -->
	<context:property-placeholder location="classpath:jdbc/jdbc.properties" ignore-unresolvable="true"/>
	
	<!-- 数据源配置 -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="${driverClassName}"></property> 
		<property name="url" value="${url}"></property> 
		<property name="username" value="${username}"></property> 
		<property name="password" value="${password}"></property> 
	</bean> 
	
	<!-- 事务配置 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />  
	</bean>

    <!--  
    <bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    -->
    <bean id="schedulerFactory" class="org.platform.modules.scheduler.factory.SchedulerFactoryExtBean">
    	<!-- Scheduler自动启动标记 -->
    	<property name="autoStartup" value="false"/>  
    	<!-- 启动延迟时间配置 -->
    	<property name="startupDelay" value="10"/>  
    	<!-- 数据源配置 -->
		<property name="dataSource" ref="dataSource"/>  
    	<!-- 事务配置 -->
    	<property name="transactionManager" ref="transactionManager"/>
    	<!-- Quartz相关配置 -->
    	<property name="configLocation" value="classpath:quartz/quartz.properties" />  
        <!-- 通过applicationContextSchedulerContextKey属性配置spring上下文 -->    
        <property name="applicationContextSchedulerContextKey" value="applicationContext"/>    
        <!-- spring管理的service需要放到这里,才能够注入成功 -->    
        <property name="schedulerContextAsMap">    
            <map>    
                <description>schedulerContextAsMap</description>    
                <entry key="schedulerBusiness" value-ref="schedulerBusiness"/>  
            </map>    
        </property>  
        <property name="triggers">              
        	<list>                  
        		<ref bean="tempTaskCronTrigger" />
        	</list>          
        </property>      
    </bean>         
    
    <!-- Trigger配置 -->
    <bean id="tempTaskCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> 
    	<!-- Trigger名称 -->
    	<property name="name" value="temp" />       
    	<!-- Trigger组名 -->
		<property name="group" value="default" />               
    	<!-- Trigger描述 -->
		<property name="description" value="TempTaskCronTrigger" />               
    	<!-- Trigger绑定的Job -->
    	<property name="jobDetail" ref="tempTaskJobDetail"/>          
    	<!-- Trigger的Cron表达式 -->
    	<property name="cronExpression" value="0/15 * * * * ?"/>      
    </bean>        
    
    <!-- Job实例配置 -->
    <bean id="tempTaskJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">  
    	<!-- Job实例名称 -->
		<property name="name" value="temp" />       
    	<!-- Job实例组名 -->
		<property name="group" value="default" />       
    	<!-- Job实例描述 -->
		<property name="description" value="TempTask" />      
    	<!-- Job实例实现类 -->
        <property name="jobClass" value="org.platform.modules.scheduler.task.TempTask" />  
    	<!-- Job实例持久化标记 -->
        <property name="durability" value="true" />     
    	<!-- Job实例恢复标记 -->
        <property name="requestsRecovery" value="true" />  
        <!-- 用来保存任何需要传递给Job实例的对象 -->     
        <property name="jobDataMap">
        	<map>
        		<entry key="timeout" value = "10" />
        		<entry key="keyword" value = "hello" />
        	</map>
        </property>
    </bean>  
	  
</beans>


SchedulerFactoryExtBean

public class SchedulerFactoryExtBean extends SchedulerFactoryBean {

	@Override
	public void afterPropertiesSet() throws Exception {
		if (isAutoStartup()) {
			super.afterPropertiesSet();
		}
	}
	
}

Quartz的简单使用

@Service("schedulerBusiness")
public class SchedulerBusinessImpl implements ISchedulerBusiness {
	
	private static final Logger LOG = LoggerFactory.getLogger(SchedulerBusinessImpl.class);
	
	@Resource(name = "schedulerFactory")
	private Scheduler scheduler = null;
	
	@SuppressWarnings("unchecked")
	@Override
	public void insert(String jobGroup, String jobName, String jobClass, String triggerGroup,
			String triggerName, String cron) throws BusinessException {
		LOG.info("Insert Scheduler {} - {} - {} - {} - {} - {} ", jobGroup, jobName, jobClass, 
				triggerGroup, triggerName, cron);
		checkParamNotNull(jobGroup, "任务组名", jobName, "任务名称", jobClass, "任务实现类", cron, "任务Cron表达式");
		try {
			JobKey jobKey = new JobKey(jobName, jobGroup);
			if (scheduler.checkExists(jobKey)) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该任务已经存在");
			} 
			if (StringUtils.isBlank(triggerGroup)) triggerGroup = jobGroup;
			if (StringUtils.isBlank(triggerName)) triggerName = jobName;
			TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup);
			if (scheduler.checkExists(triggerKey)) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该任务Trigger已经存在");
			}
			CronExpression cronExpression = new CronExpression(cron);
			ScheduleBuilder<CronTrigger> cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
			CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
					.withSchedule(cronScheduleBuilder).build();
			Class<? extends Job> jobClazz = (Class<? extends Job>) Class.forName(jobClass);
			JobDetail jobDetail = JobBuilder.newJob().withIdentity(jobKey).ofType(jobClazz)
					.storeDurably(true).requestRecovery(true).build();
			scheduler.scheduleJob(jobDetail, cronTrigger);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} 
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void updateJobCron(String jobGroup, String jobName, String cron) throws BusinessException {
		checkParamNotNull(jobGroup, "任务组名", jobName, "任务名称", cron, "任务Cron表达式");
		try {
			JobKey jobKey = new JobKey(jobName, jobGroup);
			if (!scheduler.checkExists(jobKey)) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该任务不存在");
			} 
			List<CronTrigger> cronTriggers = (List<CronTrigger>) scheduler.getTriggersOfJob(jobKey);
			if (null == cronTriggers || cronTriggers.size() == 0) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该任务不存在Trigger");
			}
			CronTrigger oldCronTrigger = cronTriggers.get(0);
			CronExpression cronExpression = new CronExpression(cron);
			ScheduleBuilder<CronTrigger> cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
			CronTrigger newCronTrigger = oldCronTrigger.getTriggerBuilder().withSchedule(cronScheduleBuilder).build();
			scheduler.rescheduleJob(oldCronTrigger.getKey(), newCronTrigger);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} 
	}
	
	@Override
	public void updateTrigger(String triggerGroup, String triggerName, String cron) throws BusinessException {
		checkParamNotNull(triggerGroup, "Trigger组名", triggerName, "Trigger名称", cron, "Cron表达式");
		try {
			TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup);
			Trigger trigger = scheduler.getTrigger(triggerKey);
			if (null == trigger) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该Trigger不存在");
			}
			CronTrigger oldCronTrigger = (CronTrigger) trigger;
			CronExpression cronExpression = new CronExpression(cron);
			ScheduleBuilder<CronTrigger> cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
			CronTrigger newCronTrigger = oldCronTrigger.getTriggerBuilder().withSchedule(cronScheduleBuilder).build();
			scheduler.rescheduleJob(triggerKey, newCronTrigger);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		} 
	}
	
	@Override
	public void pauseJob(String jobGroup, String jobName) throws BusinessException {
		checkParamNotNull(jobGroup, "任务组名", jobName, "任务名称");
		try {
			JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
			if (!scheduler.checkExists(jobKey)) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该任务不存在");
			}
			scheduler.pauseJob(jobKey);
		} catch (SchedulerException e) {
			LOG.error(e.getMessage(), e);
		}
	}
	
	@Override
	public void pauseTrigger(String triggerGroup, String triggerName) throws BusinessException {
		checkParamNotNull(triggerGroup, "Trigger组名", triggerName, "Trigger名称");
		try {
			TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup);
			if (!scheduler.checkExists(triggerKey)) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该Trigger不存在");
			}
			scheduler.pauseTrigger(triggerKey);
		} catch (SchedulerException e) {
			LOG.error(e.getMessage(), e);
		}
	}
	
	@Override
	public void resumeJob(String jobGroup, String jobName) throws BusinessException {
		checkParamNotNull(jobGroup, "任务组名", jobName, "任务名称");
		try {
			JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
			if (!scheduler.checkExists(jobKey)) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该任务不存在");
			}
			scheduler.resumeJob(jobKey);
		} catch (SchedulerException e) {
			LOG.error(e.getMessage(), e);
		}
	}
	
	@Override
	public void resumeTrigger(String triggerGroup, String triggerName) throws BusinessException {
		checkParamNotNull(triggerGroup, "Trigger组名", triggerName, "Trigger名称");
		try {
			TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup);
			if (!scheduler.checkExists(triggerKey)) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该Trigger不存在");
			}
			scheduler.resumeTrigger(triggerKey);
		} catch (SchedulerException e) {
			LOG.error(e.getMessage(), e);
		}
	}
	
	@Override
	public void delete(String jobGroup, String jobName) throws BusinessException {
		checkParamNotNull(jobGroup, "任务组名", jobName, "任务名称");
		try {
			JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
			if (!scheduler.checkExists(jobKey)) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该任务不存在");
			}
			scheduler.deleteJob(jobKey);
		} catch (SchedulerException e) {
			LOG.error(e.getMessage(), e);
		}
	}
	
	@Override
	public JobDTO readJob(String jobGroup, String jobName) throws BusinessException {
		checkParamNotNull(jobGroup, "任务组名", jobName, "任务名称");
		try {
			JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
			if (!scheduler.checkExists(jobKey)) {
				throw new BusinessException(ExceptionCode.PARAM_ERROR, "该任务不存在");
			}
			return readJobDTOByJobKey(jobKey);
		} catch (SchedulerException e) {
			LOG.error(e.getMessage(), e);
		}
		return null;
	}
	
	@Override
	public List<JobDTO> readJobs() throws BusinessException {
		List<JobDTO> jobDTOList = new ArrayList<JobDTO>();
		try {
			GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
			Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
			if (null == jobKeys || jobKeys.size() == 0) return jobDTOList;
			for (JobKey jobKey : jobKeys) {
				JobDTO jobDTO = readJobDTOByJobKey(jobKey);
				jobDTOList.add(jobDTO);
			}
		} catch (SchedulerException e) {
			LOG.error(e.getMessage(), e);
		}
		return jobDTOList;
	}
	
	private void checkParamNotNull(Object... params) {
		for (int i = 0, len = params.length; i < len; i++) {
			checkSingleParamNotNull(params[i], (String) params[++i]);
		}
	}
	
	private void checkSingleParamNotNull(Object paramValue, String paramName) {
		boolean isNull = false;
		if (paramValue instanceof String) {
			isNull = StringUtils.isBlank((String) paramValue);
		} else {
			isNull = null == paramValue ? true : false;
		}
		if (isNull) throw new BusinessException(ExceptionCode.PARAM_NULL, paramName + "不能为空");
	}
	
	@SuppressWarnings("unchecked")
	private JobDTO readJobDTOByJobKey(JobKey jobKey) {
		JobDTO jobDTO = new JobDTO();
		try {
			jobDTO.setGroup(jobKey.getGroup());
			jobDTO.setName(jobKey.getName());
			JobDetail jobDetail = scheduler.getJobDetail(jobKey);
			jobDTO.setDescription(jobDetail.getDescription());
			jobDTO.setJobClass(jobDetail.getJobClass().getName());
			jobDTO.setDurable(jobDetail.isDurable());
			jobDTO.setRequestsRecovery(jobDetail.requestsRecovery());
			List<CronTrigger> triggerList = (List<CronTrigger>) scheduler.getTriggersOfJob(jobKey);
			for (int i = 0, len = triggerList.size(); i < len; i++) {
				CronTrigger cronTrigger = triggerList.get(i);
				TriggerKey triggerKey = cronTrigger.getKey();
				TriggerDTO triggerDTO = new TriggerDTO();
				triggerDTO.setGroup(triggerKey.getGroup());
				triggerDTO.setName(triggerKey.getName());
				triggerDTO.setDescription(cronTrigger.getDescription());
				triggerDTO.setCronExpression(cronTrigger.getCronExpression());
				triggerDTO.setStartTime(cronTrigger.getStartTime());
				triggerDTO.setEndTime(cronTrigger.getEndTime());
				triggerDTO.setFinalFireTime(cronTrigger.getFinalFireTime());
				Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
				triggerDTO.setStatus(triggerState.name());
				jobDTO.getTriggers().add(triggerDTO);
			}
		} catch (SchedulerException e) {
			LOG.error(e.getMessage(), e);
		}
		return jobDTO;
	}
	
}


相关信息可以在 https://github.com/fighting-one-piece/repository-public.git 里面查看




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值