接上文,quartz采用2.2.1版本,11张数据库的表格,
1,quartz.properties 配置文件不变(跟上文一样):
#============================================================== #Configure Main Scheduler Properties #============================================================== org.quartz.scheduler.instanceName = mapScheduler org.quartz.scheduler.instanceId = AUTO #org.quartz.scheduler.instanceIdGenerator.class #============================================================== #Configure JobStore #============================================================== org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.isClustered = true org.quartz.jobStore.clusterCheckinInterval = 20000 org.quartz.jobStore.dataSource = myDS org.quartz.jobStore.maxMisfiresToHandleAtATime = 1 org.quartz.jobStore.misfireThreshold = 120000 org.quartz.jobStore.txIsolationLevelSerializable = true #============================================================== #Configure DataSource #============================================================== # org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver # org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/sdn?useUnicode=true&characterEncoding=UTF-8 # org.quartz.dataSource.myDS.user = root # org.quartz.dataSource.myDS.password = # org.quartz.dataSource.myDS.maxConnections = 30 # org.quartz.jobStore.selectWithLockSQL = SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE #============================================================== #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 #============================================================== #Skip Check Update #update:true #not update:false #============================================================== org.quartz.scheduler.skipUpdateCheck = true #============================================================================ # Configure Plugins #============================================================================ org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin org.quartz.plugin.shutdownhook.cleanShutdown = true
2,application-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" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd ">
<!-- 新加的配置 -->
<bean id="mapScheduler" lazy-init="false" autowire="no"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
destroy-method="destroy">
<!--可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->
<property name="overwriteExistingJobs" value="true" />
<!--必须的,QuartzScheduler 延时启动,应用启动完后 QuartzScheduler 再启动 -->
<property name="startupDelay" value="30" />
<!-- 设置自动启动 -->
<property name="autoStartup" value="true" />
<property name="jobFactory">
<bean class="com.ky.sdn.fk.server.quartz.SpringQuartzJobFactory"></bean>
</property>
<property name="dataSource" ref="dataSource"/>
<!-- 要记得要指定配置文件的位置 -->
<property name="configLocation" value="classpath:properties/quartz.properties" />
</bean>
</beans>
3,编写一个任务注册调度监听的类,实现ApplicationListener(由springring容器管理):
import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import org.quartz.CronScheduleBuilder; import org.quartz.CronTrigger; import org.quartz.Job; import org.quartz.JobBuilder; import org.quartz.JobDetail; import org.quartz.JobKey; import org.quartz.Scheduler; import org.quartz.TriggerBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.stereotype.Component; @Component public class QuartJobSchedulingListener implements ApplicationListener<ContextRefreshedEvent> { Logger logger = LoggerFactory.getLogger(QuartJobSchedulingListener.class); @Autowired private Scheduler scheduler; public void onApplicationEvent(ContextRefreshedEvent event) { try { ApplicationContext applicationContext = event.getApplicationContext(); this.loadCronTriggers(applicationContext, scheduler); } catch (Exception e) { logger.error(e.getMessage(), e); } } private void loadCronTriggers(ApplicationContext applicationContext, Scheduler scheduler) { Map<String, Object> quartzJobBeans = applicationContext.getBeansWithAnnotation(QuartzJob.class); Set<String> beanNames = quartzJobBeans.keySet(); List<CronTrigger> cronTriggerBeans = new ArrayList<CronTrigger>(); for (String beanName : beanNames) { Object object = quartzJobBeans.get(beanName); try { if (Job.class.isAssignableFrom(object.getClass())) { QuartzJob quartzJobAnnotation = AnnotationUtils.findAnnotation(object.getClass(), QuartzJob.class); JobKey jobKey = new JobKey(quartzJobAnnotation.name(), quartzJobAnnotation.group()); JobDetail job = JobBuilder .newJob((Class<? extends Job>) object.getClass()) .withIdentity(jobKey) .build(); CronTrigger cronTrigger = TriggerBuilder .newTrigger() .withIdentity(quartzJobAnnotation.name() + "_trigger", quartzJobAnnotation.group()) .withSchedule(CronScheduleBuilder.cronSchedule(quartzJobAnnotation.cronExp())) .build(); scheduler.scheduleJob(job, cronTrigger); } else { String errorMsg = object.getClass() + " doesn't implemented " + Job.class.getName(); logger.error(errorMsg); throw new RuntimeException(errorMsg); } } catch (Exception e) { logger.error(e.getMessage(), e); } } } }
5,编写一个SpringBeanJobFactory 的继承类
import org.quartz.Job; import org.quartz.spi.TriggerFiredBundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanWrapper; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyAccessorFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.scheduling.quartz.SpringBeanJobFactory; import org.springframework.stereotype.Component; public class SpringQuartzJobFactory extends SpringBeanJobFactory { Logger logger = LoggerFactory.getLogger(SpringQuartzJobFactory.class); @Autowired private ApplicationContext ctx; @Override @SuppressWarnings("unchecked") protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { Job job = null; try { job = ctx.getBean(bundle.getJobDetail().getJobClass()); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job); MutablePropertyValues pvs = new MutablePropertyValues(); pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap()); pvs.addPropertyValues(bundle.getTrigger().getJobDataMap()); bw.setPropertyValues(pvs, true); } catch (Exception e) { logger.error(e.getMessage(), e); throw new Exception(e); } return job; } }
6,任务定时策略的注解需要自定义去编写
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.stereotype.Component; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component //@Scope("prototype") public @interface QuartzJob { String name(); String group() default "DEFAULT_GROUP"; String cronExp(); }
7,编写任务类,用注解去实现定时
import java.util.Date;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
@QuartzJob(name = "HelloJob", cronExp = "*/10 * * * * ?")
public class HelloJob extends QuartzJobBean
{
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException
{
System.out.println("Hello Job is running @ " + new Date());
System.out.println(this.hashCode());
}
}
总结:这种方法的注解是只能一个类写同一种定时策略的n个方法,不能一个类写不同定时策略的n个方法,就算把自定义注解改为方法的注解类型,但是QuartJobSchedulingListener这个类引用了自定义注解类,能力有限,不会改源码了,所以使用具有一点点局限性,一个类只能实现同种策略的方法。
注意:容器首次正常启动后,是不会报异常信息的,再次启动时会报异常,如下:
org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'DEFAULT_GROUP.HelloJob', because one already exists with this identification.大概意思是表中XXX已经有了数据,不能继续存储进去:
大概是说数据库已经存在了这个HelloJob,也就是容器关闭时没有删除表中信息,配置这个怎么删除我也没搞懂,但是不影响使用
还有一张表需要注意:
这里面有一些cron的表达式,因为重启容器表的数据不会清空,所以改代码的cron表达式的时候记得把job的name也改成表中没有的,这个大概跟triggerName有关系的。(上一篇纯配置实现的时候是不会出现报异常的问题的,大概是已经清空了表,这点待确认一下???)