Job相关组件

提示:本文中部分内容图片节选自互联网,无意冒犯。如有侵权请私信联系作者即刻删除、更改。



Job任务接口

Job接口是自定义任务逻辑的规范,Quartz规定任务类必须实现Job接口。这是因为Schduler会通过此接口的execute方法回调具体的任务逻辑完成任务调度。Job接口中的execute()方法类似TimeTask类中的run()方法,是任务执行的入口。这是二十三种设计模式中策略模式的一种实现,如下例是一个自定义的Job接口实现类:

public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("Hello"+simpleDateFormat.format(new Date()));
    }
}

如上例中,execute()方法中有一个JobExecutionContext类型的参数context。JobExecutionContext是一个包含了各种上下文信息的对象接口,在执行Job的execute()方法时被传入。指向执行中的JobDetail 实例和执行完成的Trigger实例。
JobExecutionContext中有一个简便的方法可以获取执行上下文中的JobDataMap , 它是JobDetail 中的JobDataMap 和 Trigger 中的JobDataMap 的合并,后者的JobDataMap 会覆盖前者相同名称的值.
注意: 不要期望通过这个返回的JobDataMap 的set方法设置 、某个值持久化到作业自身的JobDataMap ,即使有@PersistJobDataAfterExecution。试图更改此映射的内容通常会导致IllegalStateException 异常。

JobExecutionContext接口

JobExecutionContext接口包含获取触发器、调度器以及MapData与数据的设置的方法规范,如同它名字它包含了任务执行时的一些上下文信息。它的方法较多这里就不一一列举,下边列举几个常用的方法如下:

  1. public Scheduler getScheduler()
    获取执行此任务的Scheduler调度器对象。
  2. public Trigger getTrigger();
    获取此任务绑定的触发器对象
  3. public Calendar getCalendar();
    获取触发器定义的任务执行时间
  4. public boolean isRecovering();
    该任务是否是暂停后,又恢复运行的状态。
  5. public TriggerKey getRecoveringTriggerKey() throws IllegalStateException;
    返回现在被恢复运行的触发器的TriggerKey,TriggerKey中包含了触发器名称、触发器组名称的一些相关信息。

还有获取执行时间、下次执行时间、执行次数等方法,使用时可以查看源码或者相关API。此参数的作用在于获取JobDataMap中的数据以及运行Job时的一些环境信息,通常配合JobDataMap将一些状态传递给Job,具体的示例请看下一章节JobDataMap。

JobDataMap

JobDataMap用于存储可序列化的对象,可以为任务执行时传递信息。它实现了java.util.Map接口,并由Quartz框架添加了一些便于存取数据的方法。向JobDataMap中存入数据的方法有两种:
第一种在JobBuilder中调用相应方法进行存取:

  1. public JobBuilder setJobData(JobDataMap newJobDataMap)
  2. public JobBuilder usingJobData(JobDataMap newJobDataMap)
  3. public JobBuilder usingJobData(String dataKey, Boolean value)
  4. public JobBuilder usingJobData(String dataKey, Double value)

    如下例中使用usingJobData方法为JobDataMap添加数据:
final JobDetail detail = JobBuilder.newJob(HelloJob.class)
        .withIdentity("job1", "group1")
        .usingJobData("cout",0)
        .usingJobData("couter",0)
        .build();

第二种就是在Job的实现类中添加变量,如果想在外部修改变量的值,只需要使用JobDataMap使用相同的key值覆盖即可。

public class HelloJob implements Job {
    private Integer cout;

    public void setCout(int cout) {
        this.cout = cout;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        cout++;
        int couter = (int) context.getJobDetail().getJobDataMap().get("couter");
        System.out.println(String.format("HelloJob时间:%s,count:%d。counter:%d", simpleDateFormat.format(new Date()), cout,couter++));
        context.getJobDetail().getJobDataMap().put("cout", cout);
        context.getJobDetail().getJobDataMap().put("couter", couter);
    }
}

需要注意如遇到同名的key值Trigger会覆盖Job中的值,尽量不要使用同名的键作为属性标识。

JobDetail

虽说Job是任务逻辑的规范,任务逻辑也是定义在自定义Job接口实现类中,但是Scheduler调度中与之交互的对象却非你定义的Job实现了,而是本章介绍的JobDetail实例。obDetail 的作用是封装 Job信息,它为 Job 添加了许多扩展参数。如:name、group、jobClass、jobDataMap等。
JobDetail定义的是任务数据,而真正执行的任务逻辑是在Job中。这样做的好处在于防止并发任务带来一些不必要的麻烦。因为使用JobDetail方式,Sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样Job实例就会被线程私有,依次规避并发访问的问题。如果Scheduler直接使用Job,就可能会存在对同一个Job实例并发访问的问题。
JobDetail是通过JobBuilder类创建的。它的获取方式如下:

JobDetail detail = JobBuilder.newJob(HelloJob.class)
        .withIdentity("job1", "group1")
        .build();

withIdetity用于定义job的相关信息,如果未指定组的名称将会使用默认的DEFAULT组,需要注意job名称必须唯一。
除此之外JobBuilder中还提供了众多设置job属性相关的方法,如任务描述、设置Data属性的重载等方法,感兴趣的小伙伴可以查看JobBuilder的源码注释了解个方法的使用或者查看官网的JavaDOC。

Job的状态

由上例可知一个Job实例在Quartz中的生命周期只限于一次任务调度。每次调度器执行Job时,会首先创建一个新的Job实例,当调用完成后,关联Job对象的实例就会被释放,进而被垃圾回收器回收。
由此可知,默认Job实例是无状态的。因为在Job调用期间持有的状态信息都是存放在JobDataMap中,而每次调用Job时都会创建一个新的JobDataMap。如果想要保持Job中的属性到下一次任务需要使用@PersistJobDataAfterExecution注解将Job持久化。如下例添加一个cout参数作为定时任务的执行次数:

@PersistJobDataAfterExecution//持久化JobDataMap对象
@DisallowConcurrentExecution//不允许并发执行
public class HelloJob implements Job {
    private Integer cout;

    public void setCout(int cout) {
        this.cout = cout;
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        cout++;
        int couter = (int) context.getJobDetail().getJobDataMap().get("couter");
        System.out.println(String.format("HelloJob时间:%s,count:%d。counter:%d", simpleDateFormat.format(new Date()), cout,couter++));
        context.getJobDetail().getJobDataMap().put("cout", cout);
        context.getJobDetail().getJobDataMap().put("couter", couter);
    }
}

需要注意到例中使用的@DisallowConcurrentExecution注解,通过对JobDetail讲解可知Quartz正是通过这种无状态的Job避免多线程并发时带来的安全行问题。所以当你决定让一个Job变为有状态时,它就已经不适合多线程了,需要使用@DisallowConcurrentExecution注解以禁用此任务被多线程执行。测试代码如下:

public static void main(String[] args) throws SchedulerException {
    final JobDetail detail = JobBuilder.newJob(HelloJob.class)
            .withIdentity("job1", "group1")
            .usingJobData("cout",0)
            .usingJobData("couter",0)
            .build();
    final SimpleTrigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("trigger1", "group1")
            .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatForever().withIntervalInSeconds(2))
            .startNow()
            .build();
    final Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
    scheduler.scheduleJob(detail,trigger);
    scheduler.start();
}

运行结果如下:
在这里插入图片描述
Job无状态任务在执行时,每次任务都会新建JobDataMap,所以数据的更改不会影响下次的执行。而有状态任务共享同一个JobDataMap实例,每次任务执行对JobDataMap所做的更改都会保存下来,每次执行任务后都会对后面的执行发生影响。正因为这个原因,无状态的Job可以并发执行,而有状态的Job不能并发执行,这意味着如果前次的Job还没有执行完毕,下一次的任务将阻塞等待,直到前次任务执行完毕。有状态任务比无状态任务需要考虑更多的因素,程序往往拥有更高的复杂度,因此除非必要,应该尽量使用无状态的Job。
另外如果Quartz使用了数据库持久化任务调度信息,无状态的JobDataMap仅会在Scheduler注册任务时保持一次,而有状态任务对应的JobDataMap在每次执行任务后都会进行保存。
另外还有一种过时的写法就是无状态Job继承Job接口,有状态Job继承StatefulJob,不过官方并不推荐使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Talend中,创建组件是通过使用Talend Studio的图形化界面来完成的。以下是创建组件的基本步骤: 1. 打开Talend Studio,并创建一个新的Talend项目。 2. 在项目中,展开“Job Designs”文件夹,并右键单击要创建组件的文件夹或子文件夹。 3. 在弹出菜单中,选择“New” > “Job”以创建一个新的作业。 4. 在作业设计的编辑器界面中,可以从左侧的“Palette”中选择不同类型的组件。 5. 在Palette中,可以选择数据集成、数据处理、文件操作等各种组件类型。 6. 通过简单的拖放操作,将所需的组件从Palette中拖放到设计编辑器的工作区中。 7. 在工作区中,连接不同组件之间的输入和输出。这可以通过拖动鼠标来创建连接线,将输出字段从一个组件的输出链接到另一个组件的输入链接。 8. 配置每个组件的属性和参数。可以通过双击组件或右键单击组件并选择“Properties”来打开组件的属性窗口,并根据需要进行配置。 9. 在设计编辑器中,可以使用其他功能和工具,如条件连接、循环等,来增强组件的逻辑和功能。 10. 最后,保存并运行创建的组件。可以通过工具栏或右键单击作业并选择“Run”来运行作业,并查看组件的执行结果。 以上就是使用Talend Studio创建组件的基本步骤。通过使用Talend Studio的图形化界面和丰富的组件库,可以快速创建和配置各种类型的组件,从而实现数据集成和处理的需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值