Home  >  Article  >  Java  >  In-depth analysis of Job and JobDetail in Quartz

In-depth analysis of Job and JobDetail in Quartz

怪我咯
怪我咯Original
2017-07-02 10:43:202321browse

The editor below will bring you an in-depth analysis of Job and JobDetail in Quartz. The editor thinks it is quite good, so I will share it with you now and give it as a reference for everyone. Let’s follow the editor and take a look.

What can Quartz be used for?

Quartz is a task scheduling framework. For example, if you encounter this problem

You want to have your credit card repaid automatically on the 25th of every month

You want to send a message to the goddess you secretly loved on April 1st every year An anonymous greeting card

I want to back up my love action movie study notes to the cloud disk every hour

The summary of these questions is: Do something at a regular time. And the time trigger conditions can be very complex (such as 17:50 on the last working day of each month), so complex that a special framework is needed to do this. Quartz is here to do this kind of thing. You give it a definition of a trigger condition, and it is responsible for triggering the corresponding job to work at the time point.

No more nonsense, the code is very good. . .

public static void main(String[] args) {
   try { //创建scheduler
  Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

  //定义一个Trigger
  Trigger trigger =TriggerBuilder.newTrigger().withIdentity("trigger1", "group1") //定义name/group
    .startNow()//一旦加入scheduler,立即生效
    .withSchedule(SimpleScheduleBuilder.simpleSchedule() //使用SimpleTrigger
      .withIntervalInSeconds(1) //每隔一秒执行一次
      .repeatForever()) //一直执行
    .build();
  //定义一个JobDetail
  JobDetail job =JobBuilder.newJob(HelloQuartz.class) //定义Job类为HelloQuartz类,这是真正的执行逻辑所在
    .withIdentity("job1", "group1") //定义name/group
    .usingJobData("name", "quartz") //定义属性
    .build();
  //加入这个调度
  scheduler.scheduleJob(job, trigger);
  //启动之
  scheduler.start();
  //运行一段时间后关闭
   Thread.sleep(10000);
   scheduler.shutdown(true);
  } catch (Exception e) {
  e.printStackTrace();
}

}

HelloQuartz class

public class HelloQuartz implements Job {
  public void execute(JobExecutionContext context) throws JobExecutionException {
    JobDetail detail = context.getJobDetail();
    String name = detail.getJobDataMap().getString("name");
    System.out.println("say hello to " + name + " at " + new Date());
  }
}


jar package:

This example covers the three most important basic elements of Quartz very well:

Scheduler: scheduler. All scheduling is controlled by it.

Trigger: Define the conditions for triggering. In the example, its type is SimpleTrigger, which is executed every 1 second (what is SimpleTrigger will be described in detail below).

JobDetail & Job: JobDetail defines task data, and the real execution logic is in the Job, in the example it is HelloQuartz. Why is it designed as JobDetail + Job instead of using Job directly? This is because tasks may be executed concurrently. If the Scheduler uses Job directly, there will be a problem of concurrent access to the same Job instance. In the JobDetail & Job method, every time sheduler is executed, a new Job instance will be created based on JobDetail, so that the problem of concurrent access can be avoided.

Scheduler

Scheduler is the brain of Quartz, and all tasks are implemented by it.

Schduelr contains two important components: JobStore and ThreadPool.

JobStore will store runtime information, including Trigger, Scheduler, JobDetail, business locks, etc. It has multiple implementations RAMJob (memory implementation), JobStoreTX (JDBC, transactions are managed by Quartz), JobStoreCMT (JDBC, using container transactions), ClusteredJobStore (cluster implementation), TerracottaJobStore (what is Terraractta).

ThreadPool is a thread pool, and Quartz has its own thread pool implementation. All tasks will be executed by the thread pool.

SchedulerFactory

SchdulerFactory, as the name suggests, is used to create Schduler. There are two implementations: DirectSchedulerFactory and StdSchdulerFactory. The former can be used to customize your own Schduler parameters in code. The latter is to directly read the quartz.properties configuration under the classpath (if it does not exist, use the default value ) to instantiate Schduler. Generally speaking, it is enough for us to use StdSchdulerFactory.

SchdulerFactory itself supports the creation of RMI stubs, which can be used to manage remote Schedulers. Its functions are the same as those of local ones, and it can submit jobs remotely.

1.job

implementation class JobDetail

JobDetail job = JobBuilder.newJob(RemindJob.class)
      .withIdentity("job1", "group1").build();//创建一个任务

  
   /**
   * 创建触发器
   * 第一种方式 不太好
   */
  SimpleTrigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "myTriggerGroup").
  withSchedule(SimpleScheduleBuilder.simpleSchedule().
      withIntervalInSeconds(3).
      repeatForever()).
      startAt(new Date(System.currentTimeMillis()+1000)).build();
  
  
  /**
   * 创建触发器
   * 第二种 方式 非常好
   * 可以 好用 2013年每月的第三个星期五上午10:30触发 0 30 10 ? * 6#3 2013
   * 2016年每月的第一个星期四下午16:17触发  0 17 16 ? * 5#1 2016
   * 每天15点到16点每5分钟运行一次,此外,每天17点到18点每5分钟运行一次 

   */
  /*CronTrigger trigger=TriggerBuilder.newTrigger() 
      .withIdentity("myTrigger", "myTriggerGroup")
      .withSchedule(CronScheduleBuilder.cronSchedule("0 18 16 ? * 5#1 2016")).build();*/
  
  SchedulerFactory sf=new StdSchedulerFactory();//创建调度者工厂
  Scheduler scheduler = sf.getScheduler();//创建一个调度者
  scheduler.scheduleJob(job,trigger);//注册并进行调度
  scheduler.start();//启动调度
  
  //Thread.sleep(millis)
  
  //scheduler.shutdown();//关闭调度

RemindJob class definition

*/
public class RemindJob implements Job {
private RemindService service=new RemindService();
  @Override
  public void execute(JobExecutionContext context) throws JobExecutionException {
    service.printPlan("你好!");
    
    Date date=new Date();
    String time = date.toString();
    System.out.println(time+"job is starting");
  }


As you can see, we pass a JobDetail instance to the scheduler. Because when we create the JobDetail, we pass the class name of the job to be executed to the JobDetail, so the scheduler Know what type of job to execute; each time the scheduler executes the job, a new instance of the class will be created before calling its execute(...) method; after execution, the reference to the instance is discarded. The instance will be garbage collected; one consequence of this execution strategy is that the job must have a parameterless

constructor (when using the default JobFactory); another consequence is that in the job class , stateful data properties should not be defined because the values ​​of these properties are not retained across multiple executions of the job.

So how to add attributes or configuration to the job instance? How to track the status of a job during multiple executions of the job? The answer is: JobDataMap, part of the JobDetail

object.

JobDataMap

JobDataMap can contain an unlimited number of (serialized) data objects, and the data can be used when the job instance is executed; JobDataMap is Java An implementation of the Map interface that adds some additional methods to facilitate access to basic types of data.

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

JobDetail job=JobBuilder.newJob(RemindJob.class)
      .withIdentity("job1", "group1")
      .usingJobData("hello", "we are family")
      .build();

在job的执行过程中,可以从JobDataMap中取出数据,如下示例:

@Override
  public void execute(JobExecutionContext context) throws JobExecutionException {
    service.printPlan("你好!");
    JobKey key=context.getJobDetail().getKey();
    
    JobDataMap map = context.getJobDetail().getJobDataMap();
    String string = map.getString("hello");
    System.out.println(key+"==========="+string);
    
    Date date=new Date();
    String time = date.toString();
    System.out.println(time+"job is starting");
  }

如果你使用的是持久化的存储机制(本教程的JobStore部分会讲到),在决定JobDataMap中存放什么数据的时候需要小心,因为JobDataMap中存储的对象都会被序列化,因此很可能会导致类的版本不一致的问题;Java的标准类型都很安全,如果你已经有了一个类的序列化后的实例,某个时候,别人修改了该类的定义,此时你需要确保对类的修改没有破坏兼容性;更多细节,参考现实中的序列化问题。另外,你也可以配置JDBC-JobStore和JobDataMap,使得map中仅允许存储基本类型和String类型的数据,这样可以避免后续的序列化问题。

如果你在job类中,为JobDataMap中存储的数据的key增加set方法(如在上面示例中,增加setJobSays(String val)方法),那么Quartz的默认JobFactory实现在job被实例化的时候会自动调用这些set方法,这样你就不需要在execute()方法中显式地从map中取数据了。

在Job执行时,JobExecutionContext中的JobDataMap为我们提供了很多的便利。它是JobDetail中的JobDataMap和Trigger中的JobDataMap的并集,但是如果存在相同的数据,则后者会覆盖前者的值。

下面的示例,在job执行时,从JobExecutionContext中获取合并后的JobDataMap:

@Override
  public void execute(JobExecutionContext context) throws JobExecutionException {
    service.printPlan("你好!");
    JobKey key=context.getJobDetail().getKey();
    
  /*  JobDataMap map = context.getJobDetail().getJobDataMap();
    String string = map.getString("hello");
    System.out.println(key+"==========="+string);*/


     JobDataMap map = context.getMergedJobDataMap();
     String string = map.getString("hello");
     System.out.println(key+"---------------------  "+string);

The above is the detailed content of In-depth analysis of Job and JobDetail in Quartz. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn