Quart(六)

185 阅读6分钟

QuartZ

Quartz是Job scheduling(作业调度)领域的一个开源项目,Quartz既可以单独使用也可以跟spring框架整合使用,在实际开发中一般会使用后者。使用Quartz可以开发一个或者多个定时任务,每个定时任务可以单独指定执行的时间,例如每隔1小时执行一次、每个月第一天上午10点执行一次、每个月最后一天下午5点执行一次等。

官网:www.w3cschool.cn/quartz_doc/

简单来说,就是可以帮助我们设置一个有规律的或者在某个具体的时间点干点想干的事的一个开源框架。

一、QuartZ核心概念

  1. Job表示一个工作,要执行的具体内容。此接口中只有一个方法,void execute(JobExecutionContext context)
  2. JobDetail表示一个具体的可执行的调度程序,Job是这个可执行调度程序所要执行的内容,另外JobDetail还包含了这个任务调度的方案和策略。
  3. Trigger代表一个调度参数的配置,什么时候去调。
  4. Scheduler代表一个调度容器,一个调度容器中可以注册多个JobDetail和Trigger。当Trigger和JobDetail组合,就可以被Scheduler容器调度了。在使用Scheduler之前,需要实例化

​ scheduler实例化后,可以启动(start)、暂停(stand-by)、停止(shutdown)。

Quartz API的关键接口是:

  • Scheduler:任务调度器,所有的任务都是从这里开始。
  • Trigger:触发器,定义任务执行的方式、间隔。
  • JobDetail & Job : 定义任务具体执行的逻辑。

Scheduler的生命期

从SchedulerFactory创建它时开始,到Scheduler调用shutdown()方法时结束;Scheduler被创建后,可以增加、删除和列举Job和Trigger,以及执行其它与调度相关的操作(如暂停Trigger)。但是,Scheduler只有在调用start()方法后,才会真正地触发trigger(即执行job)

使用示例

①创建项目 quartz_demo 导入 maven 坐标:

  <dependencies>
        <!--引入Quartz的依赖-->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
            <version>2.2.1</version>
        </dependency>
    </dependencies>

②创建QuartzTest

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzTest {
    public static void main(String[] args) {
            try {
                //定义一个JobDetail
                JobDetail jobDetail = JobBuilder.newJob(HelloQuartz.class)
                        //定义name和group 给触发器一些属性 比如名字,组名。
                        .withIdentity("job1", "group1")
                        //job需要传递的内容 具体job传递参数。
                        .usingJobData("name", "sdas")
                        .build();
                //定义一个Trigger
                Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1")
                        //加入 scheduler之后立刻执行 立刻启动
                        .startNow()
                        //定时 ,每隔1秒钟执行一次 以某种触发器触发。
                        .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1)
                                //重复执行
                                .repeatForever()).build();
                //创建scheduler
                Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
                scheduler.scheduleJob(jobDetail, trigger);
                // Scheduler只有在调用start()方法后,才会真正地触发trigger(即执行job)
                scheduler.start(); //运行一段时间后关闭
                try {
                    Thread.sleep(8000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //Scheduler调用shutdown()方法时结束
                scheduler.shutdown();
            } catch (Exception e) {
                e.printStackTrace();
            }
    }
}

③创建HelloQuartz

import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import java.util.Date;

public class HelloQuartz implements Job {
    public void execute(JobExecutionContext jobExecutionContext) {
        JobDetail detail = jobExecutionContext.getJobDetail();
        String name = detail.getJobDataMap().getString("name");
        System.out.println("my job name is  " + name + " at " + new Date());
    }
}

二、整合spring

步骤

1:创建maven工程quartz_demo,打包方式为war,导入jar包

2:自定义一个Job

3:提供Spring配置文件application-jobs.xml,配置自定义Job、任务描述、触发器、调度工厂等

4:web.xml中定义

5:启动tomcat完成测试

示例

(1)创建maven工程quartz_demo,打包方式为war,导入Quartz和spring相关坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.atguigu</groupId>
    <artifactId>quartz_demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
            <version>2.2.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <!-- 指定端口 -->
                    <port>8080</port>
                    <!-- 请求路径 -->
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

自定义一个Job

// 任务调度类
public class JobDemo {
    // 提供方法(备份数据库,清理日志,清理图片)
    public void run(){
        // 完成业务
        System.out.println(new Date());
    }
}

(3)提供Spring配置文件application-jobs.xml,配置自定义Job、任务描述、触发器、调度工厂等

  1. 创建JobDetail对象,作用是负责通过反射调用指定的Job,注入目标对象,注入目标方法
  2. 注册一个触发器,指定任务触发的时间
  3. 注册一个统一的调度工厂,通过这个调度工厂调度任务
<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                  http://www.springframework.org/schema/beans/spring-beans.xsd
                  http://www.springframework.org/schema/context
                  http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 注册自定义Job -->
    <bean id="jobDemo" class="com.atguigu.JobDemo"></bean>
    <!-- 1:创建JobDetail对象,作用是负责通过反射调用指定的Job,注入目标对象,注入目标方法 -->
    <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <!-- 注入目标对象 -->
        <property name="targetObject" ref="jobDemo"/>
        <!-- 注入目标方法 -->
        <property name="targetMethod" value="run"/>
    </bean>
    <!-- 2:注册一个触发器,指定任务触发的时间 -->
    <bean id="myTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <!-- 注入JobDetail -->
        <property name="jobDetail" ref="jobDetail"/>
        <!-- 指定触发的时间,基于Cron表达式(0/10表示从0秒开始,每10秒执行一次) -->
        <property name="cronExpression">
            <value>0/10 * * * * ?</value>
        </property>
    </bean>
    <!-- 3:注册一个统一的调度工厂,通过这个调度工厂调度任务 -->
    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!-- 注入多个触发器 -->
        <property name="triggers">
            <list>
                <ref bean="myTrigger"/>
            </list>
        </property>
    </bean>
</beans>

(4)web.xml中定义

启动web,自动加载spring容器。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         id="WebApp_ID" version="3.0">
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:application-jobs.xml</param-value>
    </context-param>
</web-app>

执行Tomcat观察控制台,可以发现每隔10秒会输出一次,说明每隔10秒自定义Job被调用一次。

三、cron表达式

上面的入门案例中我们指定了一个表达式:0/10 * * * * ?

作用:

这种表达式称为cron表达式,通过cron表达式可以灵活的定义出符合要求的程序执行的时间。如下图:

QurtZ-1.png cron表达式分为七个域,之间使用空格分割。

其中最后一个域(年)可以为空。

每个域都有自己允许的值和一些特殊字符构成。使用这些特殊字符可以使我们定义的表达式更加灵活。

特殊字符的介绍:

  • 逗号(,):指定一个值列表,例如使用在月域上1,4,5,7表示1月、4月、5月和7月
  • 横杠(-):指定一个范围,例如在时域上3-6表示3点到6点(即3点、4点、5点、6点)
  • 星号(*):表示这个域上包含所有合法的值。例如,在月份域上使用星号意味着每个月都会触发
  • 斜线(/):表示递增,例如使用在秒域上0/15表示每15秒
  • 问号(?):只能用在日和周域上,但是不能在这两个域上同时使用。表示不指定,例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用 *,如果使用 * 表示不管星期几都会触发,实际上并不是这样。
  • 井号(#):只能使用在周域上,用于指定月份中的第几周的哪一天,例如6#3,意思是某月的第三个周五 (6=星期五,3意味着月份中的第三周)
  • L:某域上允许的最后一个值。只能使用在日和周域上。当用在日域上,表示的是在月域上指定的月份的最后一天。用于周域上时,表示周的最后一天,就是星期六
  • W:W 字符代表着工作日 (星期一到星期五),只能用在日域上,它用来指定离指定日的最近的一个工作日

cron表达式在线生成器

​ 我们可以借助一些cron表达式在线生成器来根据我们的需求生成表达式即可。

cron.qqe2.com/

四、应用场景

描述

​ 上传的文件图片等,往往在表单还未提交的时候,就要触发上传的功能【因为要便于回显,如图片】,有时用户可能会选择了错误文件而放弃表单提交,此时文件服务器中就会有一批垃圾文件,占用空间。每次去手动触发清理功能就会很麻烦,需要定时进行清理。

解决方案

​ 可以通过定时任务组件定时清理这些垃圾图片。为了能够区分出来哪些图片是垃圾图片,我们在文件上传成功后将图片名保存到了一个 redis 集合中,当套餐数据插入到数据库后我们又将图片名称保存到了另一个redis集合中,通过计算这两个集合的差值就可以获得所有垃圾图片的名称。