覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Quartz - DIを使ってJobにInjectionする

QuartzでJobの定義を行うことで簡単に定期処理を記述することができますが、 デフォルトではJobにメンバ(インスタンス変数)を持たせて、それを外側からセットするのは単純にはできません。

しかし以下のようにすることで外部から必要なインスタンスをDIコンテナでInjectionすることができます。

今回のサンプルではDIコンテナにはGuiceを使用します


準備

pom.xmlにQuartzGuiceの依存関係を追加します

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.2.3</version>
</dependency>
<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>3.0</version>
</dependency>
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>


Jobの定義

コンストラクにInjectアノテーションを付与して、好きなクラスをインジェクションするように記述します
またJobのクラスにSingletonアノテーションを付与しておくことで、Jobは1インスタンスが使い回しされるようになります(インスタンスが毎回生成されることはなくなります)

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import javax.inject.Inject;
import javax.inject.Singleton;

@Singleton
public class MyJob implements org.quartz.Job {

    // インジェクションしたいクラス(インターフェース)
    private final MyLogic logic;

    @Inject
    public MyJob(MyLogic logic) {
        this.logic = logic
    }

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        logic.invoke();
    }
}


Guice経由でJobを返すJobFatoryを定義する

org.quartz.spi.JobFactoryインターフェースを実装してGuiceのDIコンテナ経由でJobを返すようにnewJobメソッドをオーバーライドします

import com.google.inject.Injector;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.spi.JobFactory;
import org.quartz.spi.TriggerFiredBundle;
import javax.inject.Inject;
import javax.inject.Singleton;

@Singleton
final class GuiceJobFactory implements JobFactory {

    private final Injector injector;

    @Inject
    GuiceJobFactory(Injector injector) {
        this.injector = injector;
    }

    @Override
    public Job newJob(TriggerFiredBundle bundle, Scheduler scheduler) throws SchedulerException {
        JobDetail jobDetail = bundle.getJobDetail();
        return injector.getInstance(jobDetail.getJobClass());
    }
}


SchedulerにGuice経由のJobFactoryをセットする

import org.quartz.Scheduler;

public interface QuartzEx {

    Scheduler getScheduler();
}


import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;


@Singleton
final class QuartzExImpl implements QuartzEx {

    private final Scheduler scheduler;

    @Inject
    QuartzExImpl(SchedulerFactory schedulerFactory, GuiceJobFactory jobFactory) throws SchedulerException {
        this.scheduler = schedulerFactory.getScheduler();
        this.scheduler.setJobFactory(jobFactory);
        this.scheduler.start();
    }

    @Override
    public final Scheduler getScheduler() {
        return this.scheduler;
    }
}


Quartz用のModuleの定義

AbstractModuleを継承してconfigureメソッドをオーバーライドして定義したGuiceJobFactory等を使用するように記述します

import com.google.inject.AbstractModule;
import org.quartz.SchedulerFactory;
import org.quartz.impl.StdSchedulerFactory;

public final class QuartzModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(SchedulerFactory.class).toInstance(new StdSchedulerFactory());
        bind(GuiceJobFactory.class);
        bind(QuartzEx.class).to(QuartzExImpl.class);
    }
}


Jobのスケジューリングを記述する

対象のJobのスケジューリングについて記述します

import com.google.inject.ImplementedBy;

@ImplementedBy(TrialQuartzImpl.class)
public interface TrialQuartz {

    void run();
}
import org.quartz.*;
import javax.inject.Inject;
import javax.inject.Singleton;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;

@Singleton
public class TrialQuartzImpl implements TrialQuartz {

    private final QuartzEx quartz;

    @Inject
    public TrialQuartzImpl(QuartzEx quartz) {
        this.quartz = quartz;
    }

    @Override
    public void run() {
        JobDetail job1 = JobBuilder.newJob(MyJob.class)
                .withIdentity("job1", "group1")
                .build();

        Trigger trigger1 = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(simpleSchedule()
                        .withIntervalInMilliseconds(100)
                        .repeatForever())
                .build();

        Scheduler scheduler = quartz.getScheduler();

        try {
            scheduler.start();
            scheduler.scheduleJob(job1, trigger1);
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }
}


Guiceを用いてアプリケーションを実行する

Guice#createInjectorを実行する際に、Quartz用に定義したQuartzModuleのインスタンスを引数に指定します。

import com.google.inject.Guice;
import com.google.inject.Injector;

public class SampleLauncher {

    public static void main(String[] args) throws Exception {
        Injector injector = Guice.createInjector(new QuartzModule());

        TrialQuartz trialQuartz = injector.getInstance(TrialQuartz.class);

        trialQuartz.run();
    }
}


これで無事にQuartz用のJobに対するインジェクションができます。



関連エントリ・リンク