首页 > 代码库 > spring+quartz,动态注册job

spring+quartz,动态注册job

 Spring+Quartz的整合有很多例子,此处不提整合;


若配置固定的job,常常使用MethodInvokingJobDetailFactoryBean,也不错, 可问题的根源在于 这个类没实现 Serializable接口, 导致了将job信息存入数据库中时,它不工作了,

这是诱因之一. 以下是文章的主要内容.


前提及目标

1.job信息存入数据库

2.可给项目添加固定的job(写在配置文件里的),也可以添加动态的job(如定时发送邮件,由JAVA代码添加job)

3.要能在实际项目中有使用价值


配置步骤开始(说白了,写程序就是写配置文件)

1. 配置DataSource, SchedulerFactoryBean

使用MYSQL

    <!--dataSource-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value=http://www.mamicode.com/"${jdbc.driverClassName}"/>>
 
    <!--schedulerFactory-->
    <bean id="schedulerFactory" lazy-init="true" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="autoStartup" value=http://www.mamicode.com/"${scheduler.auto.startup}"/>>


${scheduler.auto.startup}是一个 boolean变量, 指定是否自动启用

MYSQL数据库的SQL文件见项目others目录, 也可去Quartz的网站下载http://www.quartz-scheduler.org/


2.配置固定的Job

由于当把job信息存入数据库时需要用到序列化(Serializable),不能使用MethodInvokingJobDetailFactoryBean对象了.

建议使用CronTriggerFactoryBean, 配置如下

    <bean id="<span style="font-family: Arial, Helvetica, sans-serif;">testFixedTriggerBean</span><span style="font-family: Arial, Helvetica, sans-serif;">"</span> class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="testFixedJobDetailBean"/>
        <property name="cronExpression" value=http://www.mamicode.com/"${test.fixed.cron.expression}"/>>${test.fixed.cron.expression}是对应的 cron expression,如 0/5 * * * * ?


配置对应的JobDetailFactoryBean

    <bean id="testFixedJobDetailBean"
          class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value=http://www.mamicode.com/"com.andaily.service.scheduler.TestFixedJobDetailBean"/>>
注意: 一定要把durability=true;  jobClass即要执行任务的具体类,需要是Job的子类, 一般继承 QuartzJobBean 即可


public class TestFixedJobDetailBean extends QuartzJobBean {


    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        System.out.println("I am working on " + new Date());
    }
}


覆盖executeInternal方法, 里面写业务实现

注意: 由于此处的代码没有 事务, 若业务方法需要事务,要自己去处理下.



3.配置动态的Job

所谓动态, 就是在程序运行中, 由使用的人手动去加入的Job, 常用的如 邮件定时发送, 任务提醒等

其实质就是把Job告诉 schedulerFactory, 让其去处理

为此我们添加以下对象

DynamicSchedulerFactory   -- 管理动态Job的注册,删除等, 需要配置在xml配置文件中; 当然,肯定需要schedulerFactory对象的引用

DynamicJob                            -- 包含动态Job需要的所有信息,如 cron expression等.由程序需要时创建

public final class DynamicSchedulerFactory implements InitializingBean {

    private static final Logger LOG = LoggerFactory.getLogger(DynamicSchedulerFactory.class);
    private static Scheduler scheduler;

    public DynamicSchedulerFactory() {
    }


    /**
     * Register a job
     *
     * @param job DynamicJob
     * @return True is register successful
     * @throws SchedulerException
     */
    public static boolean registerJob(DynamicJob job) throws SchedulerException {
        final TriggerKey triggerKey = job.triggerKey();
        if (scheduler.checkExists(triggerKey)) {
            final Trigger trigger = scheduler.getTrigger(triggerKey);
            throw new SchedulerException("Already exist trigger [" + trigger + "] by key [" + triggerKey + "] in Scheduler");
        }

        final CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.cronExpression());
        final CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey)
                .withSchedule(cronScheduleBuilder)
                .build();

        final JobDetail jobDetail = job.jobDetail();
        final Date date = scheduler.scheduleJob(jobDetail, cronTrigger);

        LOG.debug("Register DynamicJob {} on [{}]", job, date);
        return true;
    }


    /**
     * Remove exists job
     *
     * @param existJob A  DynamicJob which exists in Scheduler
     * @return True is remove successful
     * @throws SchedulerException
     */
    public static boolean removeJob(DynamicJob existJob) throws SchedulerException {
        final TriggerKey triggerKey = existJob.triggerKey();
        boolean result = false;
        if (scheduler.checkExists(triggerKey)) {
            result = scheduler.unscheduleJob(triggerKey);
        }

        LOG.debug("Remove DynamicJob {} result [{}]", existJob, result);
        return result;
    }


    public void setScheduler(Scheduler scheduler) {
        DynamicSchedulerFactory.scheduler = scheduler;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(scheduler, "scheduler is null");
        LOG.info("Initial DynamicSchedulerFactory successful, scheduler instance: {}", scheduler);
    }

public class DynamicJob {


    //Job class
    private Class<? extends Job> target;

    //cron expression
    private String cronExpression;

    private String jobGroup = Scheduler.DEFAULT_GROUP;

    //Must unique
    private String jobName;


    private transient TriggerKey triggerKey;
    private transient JobDetail jobDetail;


    //default
    public DynamicJob() {
    }

    public DynamicJob(String jobName) {
        this.jobName = jobName;
    }

    public Class<? extends Job> target() {
        return target;
    }

    public DynamicJob target(Class<? extends Job> target) {
        this.target = target;
        return this;
    }


    public DynamicJob cronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
        return this;
    }

    public String jobGroup() {
        return jobGroup;
    }

    public DynamicJob jobGroup(String jobGroup) {
        this.jobGroup = jobGroup;
        return this;
    }

    public String jobName() {
        return jobName;
    }

    public DynamicJob jobName(String jobName) {
        this.jobName = jobName;
        return this;
    }

    public TriggerKey triggerKey() {
        if (triggerKey == null) {
            triggerKey = TriggerKey.triggerKey(this.jobName, this.jobGroup);
        }
        return triggerKey;
    }

    public JobDetail jobDetail() {
        if (jobDetail == null) {
            jobDetail = JobBuilder.newJob(target)
                    .withIdentity(this.jobName, this.jobGroup)
                    .build();
        }
        return jobDetail;
    }


    /*
   * Transfer data to  job
   * In job use
   *  context.getMergedJobDataMap().get(key) 
   * */
    public DynamicJob addJobData(String key, Object value) {
        final JobDetail detail = jobDetail();
        final JobDataMap jobDataMap = detail.getJobDataMap();
        jobDataMap.put(key, value);
        return this;
    }


    public String cronExpression() {
        return this.cronExpression;
    }

}


有了这两个基本的类, 实现动态的Job如下

1). 创建DynamicJob实例(jobName,cronExpression与target是必须的; target是Job类的一个实现)

2).调用DynamicSchedulerFactory的registerJob 注册一个Job或者 调用removeJob删除已添加的Job


一个示例如下:

            DynamicJob dynamicJob = createDynamicJob();
            dynamicJob.addJobData("mailGuid", mail.guid());//transfer parameter

            try {
                DynamicSchedulerFactory.registerJob(dynamicJob);
            } catch (SchedulerException e) {
                throw new IllegalStateException(e);
            }


NOTE: 关于DynamicJob中的target.

这个字段是一个 Class类型, 要求实现是 一个Job的实现类.这不具体讲了吧.



到此, 完成;  


我已将示例的代码放到GIT上, 地址: http://git.oschina.net/mkk/spring-dynamic-job 大家一起讨论,学习.






spring+quartz,动态注册job