zoukankan      html  css  js  c++  java
  • Quartz Scheduler与Spring集成(一) 基础配置与常见问题

    测试环境 Spring3.x Quartz-2.1.7

    Spring提供了很多工具类与Quartz框架集成,对集成实现了很好的支持。关于Quartz的技术细节这里不解释,这里只是讲集成的方案,并且需要对Quartz框架很了解的情况才能理解一些细节的东西。

    首先让门们先认识一下Spring提供给我们的4个工具类。

    这个类提供了对org.quartz.Scheduler的创建与配置,并且会管理它的生命周期与Spring同步。

    org.springframework.scheduling.quartz.SchedulerFactoryBean

    这个类提供创建JobDetail 并提供缺省配置。

    org.springframework.scheduling.quartz.JobDetailFactoryBean

    这个类提供创建SimpleTrigger 并提供缺省配置。

    org.springframework.scheduling.quartz.SimpleTriggerFactoryBean

    这个类提供创建CronTrigger 并提供缺省配置。

    org.springframework.scheduling.quartz.CronTriggerFactoryBean

    利用Spring提供的4个BeanFactory工具类,我们就可以对Quartz进行集成了。

    首先需要在Spring的xml当中把任务配置进去

        <bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
          <!-- 这里是你的具体任务 -->
          <property name="jobClass" value="com.gary.operation.jobdemo.demo1.HelloJob" />
          <!-- 这里要设置为true -->
          <property name="durability" value="true"></property>
        </bean>

    任务实现类

    public class HelloJob implements Job {
        @Autowired
        private SqlSession sqlSession;
        private static Logger _log = LoggerFactory.getLogger(HelloJob.class);public void execute(JobExecutionContext context)
            throws JobExecutionException {
            System.out.println("HelloJob.execute()------------------------------------");
        }
    }

    接下来我们配置一个Trigger, 这里我们使用SimpleTrigger那么我们需要使用SimpleTriggerFactoryBean来创建它

    配置的细节不解释 都是SimpleTrigger当中的属性,配置任务什么时间触发,时间间隔等。

        <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
            <property name="jobDetail" ref="exampleJob" />
            <property name="startDelay" value="1000" />
            <property name="repeatInterval" value="3000" />
            <property name="repeatCount" value="1000"/>
        </bean>

    最后我们配置Scheduler来把任务加进去,所以我们使用SchedulerFactoryBean。

    先来看一下quartz.properties由于我们使用Spring提供的数据源,所以JobStore我们不用配置。

    org.quartz.scheduler.instanceName = MyScheduler
    org.quartz.scheduler.instanceId = AUTO
    org.quartz.scheduler.skipUpdateCheck = true
    
    #============================================================================
    # Configure ThreadPool  
    #============================================================================
    org.quartz.threadPool.threadCount = 3
    org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
    org.quartz.threadPool.threadPriority: 5
    
    #============================================================================
    # Configure JobStore  
    #============================================================================
    #org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT
    #org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
    #org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
    org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.MSSQLDelegate
    org.quartz.jobStore.tablePrefix = QRTZ_
    org.quartz.jobStore.useProperties = false
    #org.quartz.jobStore.dataSource = myDS
    org.quartz.jobStore.misfireThreshold = 6000

    在看Spring.xml

        <bean name="MyScheduler"
            class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <property name="dataSource" ref="c3p0DataSource" /> <!-- 这里就设置Spring的数据源就行了 -->
             <property name="schedulerName" value="MyScheduler"></property>
             <property name="overwriteExistingJobs" value="true"></property>
            <property name="configLocation" value="classpath:quartz.properties" />
            <property name="jobFactory" ref="jobFactory"></property>
            <property name="triggers">    
                <list>
                    <ref bean="simpleTrigger"/>
                </list>
            </property>
            <property name="jobDetails">
                <list>
                    <ref bean="exampleJob"/>
                </list>
            </property>
        </bean>

    上面这样就完成了基础的配置,那么如果我们需要动态的添加一些任务该如何做呢?

    public class XXService {
    
        @Autowired
        private Scheduler scheduler;
        
        @Transactional
        public void test() throws SchedulerException {
            JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1")
                    .usingJobData("key", "jack")
                    .usingJobData("double", 13.5)
                    .build();
            Trigger trigger = newTrigger().withIdentity("trigger1", "group1")
                    // .startAt(DateBuilder.futureDate(1, IntervalUnit.MINUTE))
                    .startNow()
                    .forJob(job)
                    .withSchedule(
                            SimpleScheduleBuilder.simpleSchedule()
                                    .withIntervalInSeconds(2).withRepeatCount(2))
                    .build();
            scheduler.scheduleJob(job, trigger);
        }
    }

    其实跟正常使用quartz没有任何区别,只要在需要用Scheduler的地方进行 Autowired就行了,Spring与自动为我们注入进来。

    然后我们可以使用Spring的事物@Transactional,这样任务可以与一些其他的业务方法在同一个事物中,是不是很方便。

    还有另外一个需要注意的问题就是如果在 quartz.properties 里面配置了 org.quartz.jobStore.useProperties = true 的话 在启动Spring的时候会抛出序列化失败的异常,其原因是这样的。

    org.quartz.jobStore.useProperties = true 设置为true的时候 那么在给任务添加数据的时候就只能是字符串,在上面红色标记的地方,不能出现其他类型,但是在使用SimpleTriggerFactoryBean构建Tirgger的时候它却往里面放了引用类型,所以会导致序列化失败,源代码如下:

        public void afterPropertiesSet() throws ParseException {
            if (this.name == null) {
                this.name = this.beanName;
            }
            if (this.group == null) {
                this.group = Scheduler.DEFAULT_GROUP;
            }
            if (this.jobDetail != null) {
                this.jobDataMap.put(JobDetailAwareTrigger.JOB_DETAIL_KEY, this.jobDetail);
            }
            if (this.startDelay > 0) {
                this.startTime = new Date(System.currentTimeMillis() + this.startDelay);
            }
            else if (this.startTime == null) {
                this.startTime = new Date();
            }
    
            /*
            SimpleTriggerImpl sti = new SimpleTriggerImpl();
            sti.setName(this.name);
            sti.setGroup(this.group);
            sti.setJobKey(this.jobDetail.getKey());
            sti.setJobDataMap(this.jobDataMap);
            sti.setStartTime(this.startTime);
            sti.setRepeatInterval(this.repeatInterval);
            sti.setRepeatCount(this.repeatCount);
            sti.setPriority(this.priority);
            sti.setMisfireInstruction(this.misfireInstruction);
            this.simpleTrigger = sti;
            */
    
            Class simpleTriggerClass;
            Method jobKeyMethod;
            try {
                simpleTriggerClass = getClass().getClassLoader().loadClass("org.quartz.impl.triggers.SimpleTriggerImpl");
                jobKeyMethod = JobDetail.class.getMethod("getKey");
            }
            catch (ClassNotFoundException ex) {
                simpleTriggerClass = SimpleTrigger.class;
                jobKeyMethod = null;
            }
            catch (NoSuchMethodException ex) {
                throw new IllegalStateException("Incompatible Quartz version");
            }
            BeanWrapper bw = new BeanWrapperImpl(simpleTriggerClass);
            MutablePropertyValues pvs = new MutablePropertyValues();
            pvs.add("name", this.name);
            pvs.add("group", this.group);
            if (jobKeyMethod != null) {
                pvs.add("jobKey", ReflectionUtils.invokeMethod(jobKeyMethod, this.jobDetail));
            }
            else {
                pvs.add("jobName", this.jobDetail.getName());
                pvs.add("jobGroup", this.jobDetail.getGroup());
            }
            pvs.add("jobDataMap", this.jobDataMap);
            pvs.add("startTime", this.startTime);
            pvs.add("repeatInterval", this.repeatInterval);
            pvs.add("repeatCount", this.repeatCount);
            pvs.add("priority", this.priority);
            pvs.add("misfireInstruction", this.misfireInstruction);
            bw.setPropertyValues(pvs);
            this.simpleTrigger = (SimpleTrigger) bw.getWrappedInstance();
        }

    上面红色的地方看到了吗,他把jobDetail也放了进去,因为设置了useProperties = true 所以jobDataMap

    里面只能存放字符串所以会导致序列化失败,解决办法就是我们复写这个方法,把它删掉就行了。

    public class CustomSimpleTriggerFactoryBean extends SimpleTriggerFactoryBean {
        
        @Override
        public void afterPropertiesSet() throws java.text.ParseException {
            super.afterPropertiesSet();
            getJobDataMap().remove(JobDetailAwareTrigger.JOB_DETAIL_KEY);
        }
    }
  • 相关阅读:
    springcloud 学习笔记
    一文领悟HTTPS密钥为什么这样传输
    静态代码扫描工具PMD参数过程简介与JCommander 以及如何扩展
    静态代码扫描工具PMD分析XML的核心源码解读(从core主入口到子语言解析)
    Jenkins出现SVNException:E175002
    一个第三方登录的流程
    爬虫入门(三)爬取b站搜索页视频分析(动态页面,DBUtils存储)
    09 元素等待机制
    测试观点
    什么是请求
  • 原文地址:https://www.cnblogs.com/daxin/p/3107178.html
Copyright © 2011-2022 走看看