zoukankan      html  css  js  c++  java
  • Quartz--Spring 定时任务

    一、quartz核心概念

         先来看一张图:
        
        

    scheduler

    任务调度器

    trigger

    触发器,用于定义任务调度时间规则

    job

    任务,即被调度的任务

    misfire

    错过的,指本来应该被执行但实际没有被执行的任务调度



      • Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;
      • JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。
      • Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;
      • Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。假设,我们安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。
      • Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。
      Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler# getContext()获取对应的SchedulerContext实例;
        • ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。

    CronTrigger


     
    Quartz有两大触发器,除了上面使用的SimpleTrigger外,就是CronTrigger。CronTrigger能够提供复杂的触发器表达式的支持。CronTrigger是基于Unix Cron守护进程,它是一个调度程序,支持简单而强大的触发器语法。
     
    使用CronTrigger主要的是要掌握Cron表达式。Cron表达式包含6个必要组件和一个可选组件,如下表所示。
     

    位置

    含义

    允许的特殊字符

    1

    秒(0~59

    , -  *  /

    2

    分(0~59

    , -  *  /

    3

    小时(0~24

    , -  *  /

    4

    日期(1~31

    , -  *  /  ?  L  W  C

    5

    月(JAN~DEC1~12

    , -  *  /

    6

    星期(SUN~SAT1~7

    , -  *  /  ?  L  C  #

    7

    年(可选,1970~2099),若为空,表示全部时间范围

    , -  *  /

     
    特殊字符的含义,见下表。
     

    特殊字符

    说明

    *

    通配符,任意值

    ?

    无特定值。通常和其他指定的值一起使用,表示必须显示该值但不能检查

    -

    范围。e.g.小时部分10-12表示10:0011:00 12:00

    ,

    列分隔符。可以让你指定一系列的值。e.g.在星期域中指定MONTUEWED

    /

    增量。表示一个值的增量,e.g.分钟域中0/1表示从0开始,每次增加1min

    L

    表示Last。它在日期和星期域中表示有所不同。在日期域中,表示这个月的最后一天,而在星期域中,它永远是7(星期六)。当你希望使用星期中某一天时,L字符非常有用。e.g.星期域中6L表示每一个月的最后一个星期五

    W

    在本月内离当天最近的工作日触发,所谓的最近工作日,即当天到工作日的前后最短距离,如果当天即为工作日,则距离是0;所谓本月内指的是不能跨月取到最近工作日,即使前/后月份的最后一天/第一天确实满足最近工作日。e.g. LW表示本月的最后一个工作日触发,W强烈依赖月份。

    #

    表示该月的第几个星期,e.g. 1#2表示每一个月的第一个星期一

    C

    日历值。日期值是根据一个给定的日历计算出来的。在日期域中给定一个20C将在20日(日历包括20日)或20日后日历中包含的第一天(不包括20日)激活触发器。例如在一个星期域中使用6C表示日历中星期五(日历包括星期五)或者第一天(日历不包括星期五)

     
    Cron表达式举例:
     
    "30 * * * * ?" 每半分钟触发任务
    "30 10 * * * ?" 每小时的10分30秒触发任务
    "30 10 1 * * ?" 每天1点10分30秒触发任务
    "30 10 1 20 * ?" 每月20号1点10分30秒触发任务
    "30 10 1 20 10 ? *" 每年10月20号1点10分30秒触发任务
    "30 10 1 20 10 ? 2011" 2011年10月20号1点10分30秒触发任务
    "30 10 1 ? 10 * 2011" 2011年10月每天1点10分30秒触发任务
    "30 10 1 ? 10 SUN 2011" 2011年10月每周日1点10分30秒触发任务
    "15,30,45 * * * * ?" 每15秒,30秒,45秒时触发任务
    "15-45 * * * * ?" 15到45秒内,每秒都触发任务
    "15/5 * * * * ?" 每分钟的每15秒开始触发,每隔5秒触发一次
    "15-30/5 * * * * ?" 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
    "0 0/3 * * * ?" 每小时的第0分0秒开始,每三分钟触发一次
    "0 15 10 ? * MON-FRI" 星期一到星期五的10点15分0秒触发任务
    "0 15 10 L * ?" 每个月最后一天的10点15分0秒触发任务
    "0 15 10 LW * ?" 每个月最后一个工作日的10点15分0秒触发任务
    "0 15 10 ? * 5L" 每个月最后一个星期四的10点15分0秒触发任务
    "0 15 10 ? * 5#3" 每个月第三周的星期四的10点15分0秒触发任务

    项目的依赖:

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

    在Spring中使用Quartz有两种方式实现:第一种是任务类继承QuartzJobBean,第二种则是在配置文件里定义任务类和要执行的方法,类和方法可以是普通类。很显然,第二种方式远比第一种方式来的灵活。

    /**
     * 定时器配置
     *
     */

    @Configuration
    public class SpringTaskConfig {

      /**
         * 定时任务
         *
         * @return
         */
        @Bean
        public JobDetailFactoryBean jobDaySettleAgentMonthlyDetail() {

        private static final String TASK_MONTHLY_CRON_EXPRESSION = "0 01 00 1 * ?";// 每月1日凌晨00:01分钟执行一次


            JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
            jobDetailFactoryBean.setJobClass(TaskMonthlyJob.class);    //任务类
            jobDetailFactoryBean.setDurability(true);
            jobDetailFactoryBean.setRequestsRecovery(true);
            return jobDetailFactoryBean;
        }

        @Bean
        public CronTriggerFactoryBean settleAgentMonthlyTrigger() {
            CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
            cronTriggerFactoryBean.setJobDetail(jobDaySettleAgentMonthlyDetail().getObject());
            cronTriggerFactoryBean.setCronExpression(TASK_MONTHLY_CRON_EXPRESSION);
            return cronTriggerFactoryBean;
        }

    }

    任务类:

    /**
     * 定时任务
     *
     */
    @PersistJobDataAfterExecution
    @DisallowConcurrentExecution
    // 不允许并发执行
    public class TaskMonthlyJob extends QuartzJobBean {

      @Override
        protected void executeInternal(JobExecutionContext jobexecutioncontext)
                throws JobExecutionException {

        *****任务代码****

      }

    }

  • 相关阅读:
    smtplib文字邮件的发送
    接口测试框架分析
    UnitTest框架的快速构建与运行
    Appium + Python App自动化第一个脚本
    3个必备cookie实用方法
    单元测试框架的选择
    快速构建一个完整的Selenium框架
    SSH原理与运用
    springboot 集成 spring-data-elasticsearch
    Java 集合排序
  • 原文地址:https://www.cnblogs.com/guxia/p/6625514.html
Copyright © 2011-2022 走看看