Spring中使用定时任务Quartz

需要加入的jar包:

<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

Quartz的相关核心概念:
1、Job:是一个接口,只有一个execute(JobExecutionContext var1)方法,开发者通过实现该接口来定义需要执行的任务。Job运行时的信息保存在JobDataMap实例中。

2、JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不是直接接收一个Job实例,而是接收一个Job实现类,以便通过反射机制实例化Job,因此需要一个类来描述Job的实现类及其相关的静态信息(Job名称、描述等)。

3、Trigger:一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或以固定间隔周期性执行时,可以使用SimpleTrigger。当需要复杂的调度方案,可以使用CronTrigger。

4、Calendar:org.quartz.Calendar和java.util.Calendar不一样,它是一些日历特定时间点的集合(每周、每年、五一、十一)。java.util.Calendar代表一个日历时间点(20171001)。

5、Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,二者在Scheduler中拥有各自的组和名称。组及名称是Scheduler查找定位容器中某一对象的依据。因此,组及名称必须唯一(但是Trigger和JobDetail的组和名称可以相同,因为它们是不同的类型)。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。

Job有一个StatefulJob子接口,代表有状态的任务。它是一个没有方法的标签接口,目的是让Quartz知道任务的类型,以便执行不同的方案。

无状态任务在执行时拥有自己的JobDataMap复制,对JobDataMap的更改不会影响下次的执行。而有状态任务共享同一个JobDataMap实例,每次执行任务对JobDataMap的更改会保存下来,会对后面的执行产生影响。

因此,无状态的Job可以并发执行,而有状态的StatefulJob不能并发执行。如果前次的StatefulJob还没有执行完毕,则下次的任务将阻塞等待,直到前次任务执行完毕。避免使用有状态的Job。

使用数据库持久化任务调度信息,则无状态的JobDataMap仅在Scheduler注册任务时保存一次,而有状态的任务的JobDataMap在每次执行任务后都会进行保存。

一、Spring中使用Quartz

1.1、JobDetailFactoryBean
使用该Bean声明JobDetail时,Bean的名字即是任务的名字,如果没有指定所属组,就使用默认组。

<bean name="jobdetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <!--实现Job接口的任务类-->
        <property name="jobClass" value="com.smart.service.SampleJob"/>
        <!--设置一个键,将Spring的ApplicationContext的引用保存到JobDataMap中,以便在Job的代码中访问-->
        <property name="applicationContextJobDataKey" value="applicationContext"/>
        <!--为任务所对应的JobDataMap提供值-->
        <property name="jobDataAsMap">
            <map>
                <entry key="size" value="10"/>
            </map>
        </property>
    </bean>

任务类:

public class SampleJob implements Job {
    public void execute(JobExecutionContext jtx) throws JobExecutionException {
       Map dataMap=jtx.getJobDetail().getJobDataMap();
        //获取保存的值
        String size=(String)dataMap.get("size");
        //获取Spring的上下文信息,可以访问容器中的其他任何Bean
        ApplicationContext ctx=(ApplicationContext)dataMap.get("applicationContext");
        //对JobDataMap中保存的值进行修改,要根据任务类型进行区分
         //如果实现Job接口,这种更改对于下一次执行是不可见的
        //如果实现StatefulJob接口,对于下一次执行是可见的
        dataMap.put("size",size+"0");
    }
}

1.2、MethodInvokingJobDetailFactoryBean
可以将一个Bean的某个方法封装成满足Quartz要求的Job,但是产生的JobDetail不能被序列化,所以不能被持久化到数据库中。

<bean id="sampleDetailJob" class="com.smart.service.SampleDetailJob"/>

    <bean id="jobDetail_1" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <!--引用一个Bean-->
        <property name="targetObject" ref="sampleDetailJob"/>
       <!--指定目标Bean的方法,可以是static方法和非static方法,但是不能拥有参数-->
        <property name="targetMethod" value="execute"/>
        <!--指定任务是否有状态  false:不并发,说明有状态 -->
        <property name="concurrent" value="false"/>
    </bean>

2、创建Trigger
2.1、SimpleTriggerFactoryBean
在默认情况下,通过SimpleTriggerFactoryBean配置的Trigger名称即为Bean的名称,属于默认值。

 <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <!--对应的JobDetail-->
        <property name="jobDetail" ref="jobdetail"/>
        <property name="startDelay" value="1000"/>
         <property name="repeatCount" value="100"/>
        <property name="repeatInterval" value="2000"/>
        <property name="jobDataAsMap">
            <map>
                <entry key="count" value="10"/>
            </map>
        </property>
    </bean>

在Job中获取Trigger中的dataMap的值:

     Map triggerMap=jtx.getTrigger().getJobDataMap();
        String count=(String)triggerMap.get("count");
        //对dataMap的更改不会持久化,也不影响下次的执行
        triggerMap.put("count","30");

2.2、CronTriggerFactorBean
新增的属性和配置与SimpleTriggerFactoryBean相似,

 <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="jobdetail"/>
        <property name="cronExpression" value="0 0 5 * * * ?"/>
    </bean>

3、创建Scheduler
3.1、SchedulerFactoryBean

 <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
       <!--初始化完成后,延迟10秒启动,默认是0,立即启动,一般情况下,延迟一小段时间启动,以便Spring可以初始化剩余的Bean-->
       <property name="startupDelay" value="10"/>
        <!--配置多个trigger-->
        <property name="triggers">
            <list>
                <ref bean="cronTrigger"/>
                <ref bean="simpleTrigger"/>
            </list>
        </property>
        <property name="schedulerContextAsMap">
            <map>
                <entry key="timeout" value="30"/>
            </map>
        </property>
        <!--Quartz本身有一个默认的配置文件,也可以自己设置自定义的Quartz的配置文件覆盖默认的-->
        <property name="configLocation" value="classpath:quartz.properties"/>
        <!--也可以直接通过属性配置,而不是配置文件-->
        <property name="quartzProperties">
            <props>
             <!--数据保存到数据库而不是内存中-->
                <prop key="org.quartz.jobStore.class">
                    org.quartz.impl.jdbcjobstore.JobStoreTX
                </prop>
            </props>
        </property>
    </bean>

二、普通的创建定时任务

2.1、创建 JobDetail

public JobDetail createJob(Task t) {
        //任务执行的Job
        JobBuilder jobBuiler = JobBuilder.newJob(WebCollectJob.class);
        JobDataMap dataMap = new JobDataMap();
        dataMap.put("service", service);
        dataMap.put("Task", t);
        jobBuiler.usingJobData(dataMap);
        jobBuiler.withIdentity(DEFAULT_JOB_KEY + t.getId(), DEFAULT_JOB_GROUP);
        JobDetail jobDetail = jobBuiler.build();
        return jobDetail;
    }

2.2、创建 Trigger

public Trigger createTrigger(Task t) throws ParseException {
        TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
        String name = DEFAULT_TRIGFER_KEY + t.getId();
        triggerBuilder.withIdentity(name, DEFAULT_JOB_GROUP);
        triggerBuilder.withSchedule(getSimpleSchedule(t.getTaskInterval() * 60 * 60 * 1000));
        triggerBuilder.startNow();
        Trigger trigger = triggerBuilder.build();
        return trigger;
    }

2.3、创建 Scheduler

//配置
<bean id="abstractSchedule" abstract="true"
          class="com.jolly.spider.core.job.AbstractScheduleService">
        <property name="schedulerFactory" ref="schedulerFactory" />
    </bean>
    <bean id="schedulerFactory" class="org.quartz.impl.StdSchedulerFactory">
        <constructor-arg>
        <!--覆盖默认的配置-->
            <value>quartz.properties</value>
        </constructor-arg>
    </bean>
//获取
private Scheduler        scheduler;

    private SchedulerFactory schedulerFactory;

    public Scheduler getScheduler() {
        return scheduler;
    }

    public void setScheduler(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    public SchedulerFactory getSchedulerFactory() {
        return schedulerFactory;
    }

    public void setSchedulerFactory(SchedulerFactory schedulerFactory) {
        this.schedulerFactory = schedulerFactory;
        try {
            setScheduler(this.schedulerFactory.getScheduler());
        } catch (SchedulerException e) {
            logger.error("获取Scheduler失败.", e);
        }
    }

2.4、执行 任务

public boolean createScheduler(Task task) {
        try {
            JobDetail jobDetail = createJob(task);
            Trigger trigger = createTrigger(task);
            //代码片段。getScheduler()返回 Scheduler 对象
            getScheduler().scheduleJob(jobDetail, trigger);
            getScheduler().start();
            return true;
        } catch (Exception e) {
            logger.error("任务[{}]启动失败", task.getId(), e);
        }
        return false;
    }

Quartz Properties 文件

#主要分为四个部分 线程池属性,调度器属性,作业存储设置,插件配置 
#===============================================================     
# 1、  Configure ThreadPool     线程池属性
#===============================================================   
#处理Job的线程个数,至少为1,但最多的话最好不要超过100,在多数机器上设置该值超过100的话就会显得相当不实用了,特别是在你的 Job 执行时间较长的情况下
org.quartz.threadPool.threadCount =  5     
#线程的优先级,优先级别高的线程比级别低的线程优先得到执行。最小为1,最大为10,默认为5
org.quartz.threadPool.threadPriority = 5 
#一个实现了 org.quartz.spi.ThreadPool 接口的类,Quartz 自带的线程池实现类是 org.quartz.smpl.SimpleThreadPool      (threadPool类名(SimpleThreadPool就好))
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool  



#===============================================================     
#  2、 Configure JobStore  作业存储设置
#===============================================================      
#要使 Job 存储在内存中需通过设置  org.quartz.jobStrore.class 属性为 org.quartz.simpl.RAMJobStore ,
#将schedule相关信息保存在RAM中,轻量级,速度快,遗憾的是应用重启时相关信息都将丢失。
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore  
# misfireThreshold : 最大能忍受的触发超时时间,如果超过则认为“失误”
org.quartz.jobStore.misfireThreshold = 60000



#===============================================================     
#  3、  Configure Main Scheduler Properties     调度器属性
#===============================================================  
#调度器的实例名     
org.quartz.scheduler.instanceName = QuartzScheduler     
#调度器的实例ID,大多数情况设置为auto即可  
org.quartz.scheduler.instanceId = AUTO 
org.quartz.scheduler.rmi.export=false
org.quartz.scheduler.rmi.proxy=false
org.quartz.scheduler.wrapJobExecutionInUserTransaction=false 



#===============================================================     
#   4、 Configure Plugins    插件配置 
#===============================================================       
org.quartz.plugin.jobInitializer.class =       
org.quartz.plugins.xml.JobInitializationPlugin       

org.quartz.plugin.jobInitializer.overWriteExistingJobs = true      
org.quartz.plugin.jobInitializer.failOnFileNotFound = true      
org.quartz.plugin.jobInitializer.validating=false  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值