Quartz(2) Quartz API

Quartz API的关键接口是:

  • Scheduler - 与调度程序交互的主要API。
  • Job - 由希望由调度程序执行的组件实现的接口。
  • JobDetail - 用于定义作业的实例。
  • Trigger(即触发器) - 定义执行给定作业的计划的组件。
  • JobBuilder - 用于定义/构建JobDetail实例,用于定义作业的实例。
  • TriggerBuilder - 用于定义/构建触发器实例。

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

Quartz提供的“builder”类,可以认为是一种领域特定语言(DSL,Domain Specific Language)。教程一中有相关示例,这里是其中的代码片段:(校对注:这种级联的API非常方便用户使用,大家以后写对外接口时也可以使用这种方式)

// define the job and tie it to our HelloJob class
  JobDetail job = newJob(HelloJob.class)
      .withIdentity("myJob", "group1") // name "myJob", group "group1"
      .build();

  // Trigger the job to run now, and then every 40 seconds
  Trigger trigger = newTrigger()
      .withIdentity("myTrigger", "group1")
      .startNow()
      .withSchedule(simpleSchedule()
          .withIntervalInSeconds(40)
          .repeatForever())            
      .build();

  // Tell quartz to schedule the job using our trigger
  sched.scheduleJob(job, trigger);

Job和Trigger

一个job就是一个实现了Job接口的类,该接口只有一个方法:

Job接口:

package org.quartz;

  public interface Job {

    public void execute(JobExecutionContext context)
      throws JobExecutionException;
  }

当Job的一个trigger被触发(稍后会讲到)时,execute()方法由调度程序的一个工作线程调用。传递给execute()方法的JobExecutionContext对象向作业实例提供有关其“运行时”环境,该job的一个trigger被触发后(稍后会讲到),execute()方法会被scheduler的一个工作线程调用;传递给execute()方法的JobExecutionContext对象中保存着该job运行时的一些信息 ,执行job的scheduler的引用,触发job的trigger的引用,JobDetail对象引用,以及一些其它信息。

JobDetail对象是在将job加入scheduler时,由客户端程序(你的程序)创建的。它包含job的各种属性设置,以及用于存储job实例状态信息的JobDataMap。

Trigger用于触发Job的执行。当你准备调度一个job时,你创建一个Trigger的实例,然后设置调度相关的属性。Trigger也有一个相关联的JobDataMap,用于给Job传递一些触发相关的参数。Quartz自带了各种不同类型的Trigger,最常用的主要是SimpleTrigger和CronTrigger。

为什么既有Job,又有Trigger呢?很多任务调度器并不区分Job和Trigger。有些调度器只是简单地通过一个执行时间和一些job标识符来定义一个Job;其它的一些调度器将Quartz的Job和Trigger对象合二为一。在开发Quartz的时候,我们认为将调度和要调度的任务分离是合理的。在我们看来,这可以带来很多好处。

例如,Job被创建后,可以保存在Scheduler中,与Trigger是独立的,同一个Job可以有多个Trigger;这种松耦合的另一个好处是,当与Scheduler中的Job关联的trigger都过期时,可以配置Job稍后被重新调度,而不用重新定义Job;还有,可以修改或者替换Trigger,而不用重新定义与之关联的Job。

Key

将Job和Trigger注册到Scheduler时,可以为它们设置key,配置其身份属性。Job和Trigger的key(JobKey和TriggerKey)可以用于将Job和Trigger放到不同的分组(group)里,然后基于分组进行操作。同一个分组下的Job或Trigger的名称必须唯一,即一个Job或Trigger的key由名称(name)和分组(group)组成。

 public class HelloJob implements Job {

    //构造函数 或者该job 不定义属性参数。防止对象属性值在jvm 垃圾回收后 数据丢失。
    public HelloJob() {
    }

    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      System.err.println("Hello!  HelloJob is executing.");
    }
  }

可以看到,我们传给scheduler一个JobDetail实例,因为我们在创建JobDetail时,将要执行的job的类名传给了JobDetail,所以scheduler就知道了要执行何种类型的job;每次当scheduler执行job时,在调用其execute(…)方法之前会创建该类的一个新的实例;执行完毕,对该实例的引用就被丢弃了,实例会被垃圾回收;这种执行策略带来的一个后果是,job必须有一个无参的构造函数(当使用默认的JobFactory时);另一个后果是,在job类中,不应该定义有状态的数据属性,因为在job的多次执行中,这些属性的值不会保留。

那么如何给job实例增加属性或配置呢?如何在job的多次执行中,跟踪job的状态呢?

答案就是:JobDataMap,JobDetail对象的一部分。

JobDataMap

JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法。

将job加入到scheduler之前,在构建JobDetail时,可以将数据放入JobDataMap,如下示例:

jobDetail.getJobDataMap().put("param", "xxxxxx");

如果你使用的是持久化的存储机制,在决定JobDataMap中存放什么数据的时候需要小心,因为JobDataMap中存储的对象都会被序列化,因此很可能会导致类的版本不一致的问题;Java的标准类型都很安全,如果你已经有了一个类的序列化后的实例,某个时候,别人修改了该类的定义,此时你需要确保对类的修改没有破坏兼容性;

JobExecutionException

最后,是关于Job.execute(..)方法的一些额外细节。execute方法中仅允许抛出一种类型的异常(包括RuntimeExceptions),即JobExecutionException。因此,你应该将execute方法中的所有内容都放到一个”try-catch”块中。你也应该花点时间看看JobExecutionException的文档,因为你的job可以使用该异常告诉scheduler,你希望如何来处理发生的异常。

 

=======================模板 逻辑代码=============================

0、quartz.properties

# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#

org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

#ThreadPool mode
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

#new Thread mode
#org.quartz.threadPool.class = gov.gfmis.fap.framework.service.autotask.ZeroSizeThreadPool

org.quartz.jobStore.misfireThreshold = 60000

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

1、根据SchedulerFactory 获取scheduler(调度器)

Properties props = new Properties();
//读取quartz.properties 配置文件信息 获取需要的props对象信息 构造SchedulerFactory
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("quartz.properties");
        props.load(in);
//根据props配置信息 构造schedulerFactory工厂对象 用于创建scheduler 
        schedulerFactory = new ExtendsStdSchedulerFactory(props);
//根据schedulerFactory工厂对象 获取scheduler 对象
        scheduler = schedulerFactory.getScheduler();
//自定义SchedulerListenerSupport 
        AutoJobScheduleListener scheduleListener = new AutoJobScheduleListener();
//调度器  scheduler中 加入SchedulerListener     
        scheduler.addSchedulerListener(scheduleListener);
//自定义JobListener
        AutoJobJobListener jobListener = new AutoJobJobListener();
        jobListener.setName(JobListenerName);
        jobListener.setGroupName("JobListenerGroup");
//调度器  scheduler中 加入jobListener 
        scheduler.addJobListener(jobListener);
//自定义TriggerListener
        AutoJobTriggerListener triggerListener=new AutoJobTriggerListener();
//调度器  scheduler中 加入triggerListener
        scheduler.addTriggerListener(triggerListener);
//启动scheduler
        scheduler.start();

2、job 加入到scheduler   用于被控制。

//根据job的实现类bean来获取类信息 用于构造jobDetail 
Object obj=ServiceFactory.getBean(beanName);
Class bean = obj.getClass();

// 初始化作业
JobDetail jobDetail = new JobDetail("jobName", "jobGroup", bean);
// 往jobDetail 中添加自定义业务参数,用于后续需要的业务逻辑处理
jobDetail.getJobDataMap().put("param", "xxxxxxxxx");
// 实例化任务监听类
jobDetail.addJobListener("JobListener");
// 调度规则类  创建定时触发器SimpleTrigger 或CronTrigger 两种类型
//1.SimpleTrigger
SimpleTrigger simpleTrigger = new SimpleTrigger("jobName", "jobGroup");
//设置simpleTrigger 的执行规则 如启动时间和结束时间 间隔时间 执行次数等
。。。
//给simpleTrigger 增加自定义autoJobTriggerListener trigger监听器
simpleTrigger.addTriggerListener("autoJobTriggerListener");
// 注册任务
scheduler.scheduleJob(jobDetail, simpleTrigger);
//2.CronTrigger 
CronExpression cexp = new CronExpression("规则表达式".toString());
CronTrigger cronTrigger = new CronTrigger("jobName", "jobGroup");
// 设置Cron表达式
cronTrigger.setCronExpression(cexp);
//给cronTrigger 增加autoJobTriggerListener trigger监听器
cronTrigger.addTriggerListener("autoJobTriggerListener");
// 注册任务
scheduler.scheduleJob(jobDetail, cronTrigger);

//注意Job、JobDetail、Trigger的key可自定义 不一定非得是"jobName", "jobGroup"

3、各种Listener

public class AutoJobScheduleListener extends SchedulerListenerSupport {
      /**
       * 重载自动任务挂起监听事件
       * @param jobName
       *                String 任务名称
       * @param groupName
       *                String 任务组名称
       * 
       */
  public void jobsPaused(String jobName, String groupName) {

  }

      /**
       * 重载自动任务恢复运行监听事件
       * 
       * @param jobName
       *                String 任务名称
       * @param groupName
       *                String 任务组名称
       * 
       */
  public void jobsResumed(String jobName, String groupName) {

  }

}


public class AutoJobTriggerListener implements TriggerListener {
    private static final Logger log = Logger.getLogger(AutoJobTriggerListener.class);
    @Override
    public String getName() {
        return "autoJobTriggerListener";
    }
    /**
     * (1)
     * Trigger被激发 它关联的job即将被运行
     * Called by the Scheduler when a Trigger has fired, and it's associated JobDetail is about to be executed.
     */
      @Override
    public void triggerFired(Trigger trigger, JobExecutionContext jobExecutionContext) {

    }

    /**
     * (2)
     * Trigger被激发 它关联的job即将被运行,先执行(1),在执行(2) 如果返回TRUE 那么任务job会被终止
     * Called by the Scheduler when a Trigger has fired, and it's associated JobDetail is about to be executed
     */
    @Override
    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext jobExecutionContext) {
        // 获取job的名字  autotask_id
        Long taskId = Long.parseLong(jobExecutionContext.getJobDetail().getName());
        

        if(!是否分布式执行的开关判断位置(分布式锁)){
            log.debug("===Trigger监听器:AutoJobTriggerListener.vetoJobExecution()===自动任务id为:"+taskId+"===");
            return true;
        }
        return false;
    }
    /**
     * (3) 当Trigger错过被激发时执行,比如当前时间有很多触发器都需要执行,但是线程池中的有效线程都在工作,
     *  那么有的触发器就有可能超时,错过这一轮的触发。
     * Called by the Scheduler when a Trigger has misfired.
     */

    @Override
    public void triggerMisfired(Trigger trigger) {
        log.info("Trigger监听器:AutoJobTriggerListener.triggerMisfired()");
    }
/**
 * (4) 任务完成时触发
 * Called by the Scheduler when a Trigger has fired, it's associated JobDetail has been executed
 * and it's triggered(xx) method has been called.
 */

    @Override
    public void triggerComplete(Trigger trigger, JobExecutionContext jobExecutionContext, int i) {
        log.debug("===Trigger监听器:AutoJobTriggerListener.triggerComplete()==="+jobExecutionContext.getJobDetail().getName()+"自动任务任务触发完成===");
    }
}


public class AutoJobJobListener implements JobListener {
	private static final Logger logger = Logger.getLogger(AutoJobJobListener.class);

	
	String name = null;

	String groupName = null;

	/**
	 * @return groupName
	 */
	public String getGroupName() {
		return groupName;
	}

	/**
	 * @param groupName
	 *            要设置的 groupName
	 */
	public void setGroupName(String groupName) {
		this.groupName = groupName;
	}

	/**
	 * @return name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            要设置的 name
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * 重载“自动任务执行完成事件”
	 * 
	 * @param context
	 *            JobExecutionContext 上下文环境
	 * @param ex
	 *            JobExecutionException 自动任务异常抛出
	 * 
	 */
	public void jobWasExecuted(JobExecutionContext context, JobExecutionException ex) {
		logger.debug("===AutoJobJobListener.jobWasExecuted===");
	
		// 获取job的名字
		int job_id = Integer.parseInt(context.getJobDetail().getName());
		
		try {
			if (ex != null) {
				logger.debug("===AutoJobJobListener.jobWasExecuted===错误执行的异常信息为:"+ex.getMessage());
          //job错误执行的日志记录逻辑(写入数据库持久化错误日志信息,方便后续查找job错误执行原因)
			} else {
				//job 正确执行的持久化写日志逻辑。
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		finally{
		
			//移除AutoJobTriggerListener.vetoJobExecution()方法中判断逻辑 建立的分布式锁。
            
		}
	}

	/**
	 * 实现“执行被拒绝的响应事件”
	 * 
	 * 不做任何操作
	 * 
	 * @param jobexecutioncontext
	 *            JobExecutionContext 上下文环境
	 * 
	 */
	public void jobExecutionVetoed(JobExecutionContext jobexecutioncontext) {
		logger.debug("===AutoJobJobListener 监听器:AutoJobJobListener.jobExecutionVetoed()===该任务:"+jobexecutioncontext.getJobDetail().getName()+"====");
	}

	/**
	 * 实现“即将执行的响应事件”
	 * 
	 * @param jobexecutioncontext
	 *            JobExecutionContext 上下文环境
	 * 
	 */
	public void jobToBeExecuted(JobExecutionContext jobexecutioncontext) {
		
		// 获取job的名字
		Long taskId = Long.parseLong(jobexecutioncontext.getJobDetail().getName());
		
		
	}

	
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值