기존 프로젝트에 배치 기능을 추가한 적이 있습니다.
선배님께서 Quartz Scheduler를 공부해보고 개발에 적용해보라는 말씀에 Quartz Scheduler를 공부하고 개발 및 배포까지 수행했습니다.
관련해서 다시 공부하기 위해 적는 글입니다.
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
pom.xml에 dependency를 추가합니다.
만약 Library를 사용할 경우 jar를 다운받아 추가합니다.
Quartz는 일반 클래스로 동작하기 때문에 Spring DI를 사용할 수 없습니다.
따라서 @Autowired를 통해 Service를 호출하여도 null이 출력되며 동작하지 않습니다.
이를 해결하기 위해 Job 과 Task를 분리하여 코드를 작성해보겠습니다.
package com.study.common.batch.test;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class TestBatchJob extends QuartzJobBean {
private TestBatchTask testBatchTask;
@Autowired
public void setTestBatchTask(TestBatchTask testBatchTask) {
this.testBatchTask = testBatchTask;
}
@Override
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
testBatchTask.task();
}
}
@Autowired
public void setTestBatchTask(TestBatchTask testBatchTask) {
this.testBatchTask = testBatchTask;
}
Task를 주입하여 Spring DI 컨테이너가 사용할 수 있는 실제 작업이 일어나는 객체입니다.
@Override
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
testBatchTask.task();
}
스케줄러에 의해 Job 클래스가 호출되었을 때 해당 executeInternal 메소드를 실행합니다.
package com.study.common.batch.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.study.common.service.CommonService;
public class TestBatchTask {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
/** Annotation 사용 가능 */
@Autowired
CommonService commonService;
/** 실제로 Scheduling 되는 메소드 */
public void task() {
log.info("Spring Quartz Scheduler executed!");
try {
/* Task 작성 */
} catch (Exception e) {
logger.error("Error : {}", e.getMessage());
}
}
}
빈(Bean)으로 등록된 TestBatchTask 객체는 의존성 주입을 통해 Service 객체 사용이 가능합니다.
실제 스케줄링이 일어났을 때(executeInternal 참조) task() 메소드가 실행됩니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Task bean 생성 -->
<bean id="testBatchTask" class="com.study.common.batch.TestBatchTask" />
<!-- Job -->
<bean name="testBatchJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.study.common.batch.TestBatchJob" />
<!-- Task -->
<property name="jobDataAsMap">
<map>
<entry key="testBatchTask" value-ref="testBatchTask" />
</map>
</property>
</bean>
<!-- quartz trigger 작성 -->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="testBatchJob" />
<!-- 1분마다 실행 -->
<property name="cronExpression" value="0 0/1 * * * ?" />
</bean>
<!-- quartz trigger 실행 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger"/>
</list>
</property>
</bean>
</beans>
<!-- Task bean 생성 -->
<bean id="testBatchTask" class="com.study.common.batch.TestBatchTask" />
Spring IoC 컨테이너가 관리할 수 있도록 TestBatchTask 객체를 빈(Bean)으로 등록
<!-- Job -->
<bean name="testBatchJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.study.common.batch.TestBatchJob" />
<!-- Task -->
<property name="jobDataAsMap">
<map>
<entry key="testBatchTask" value-ref="testBatchTask" />
</map>
</property>
</bean>
JobDetailFactoryBean 클래스에 스케줄링하기 위한 Job 클래스를 주입합니다.
jobDataAsMap에 Service를 작성하는 것이 아니라 실제로 작업할 Task 하나만 작성하여 사용한다.(작업하는데 필요한 빈 객체(Bean)를 value-ref를 통해 참조)
<!-- quartz trigger 작성-->
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="testBatchJob" />
<!-- 1분마다 실행 -->
<property name="cronExpression" value="0 0/1 * * * ?" />
</bean>
<!-- quartz trigger 실행-->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger"/>
</list>
</property>
</bean>
스케줄러는 CronTrigger 방식으로 사용했으며, cronExpression을 설정하여 어떤 주기를 설정합니다.
이후 SchedulerFactoryBean을 통해 트리거를 등록해줍니다.
Quartz를 통해 Batch 작업을 구현하는 방법에 대해 공부하고 작성한 글입니다.
실제 작업에서 변경 또는 추가한 내용
Quartz를 공부하면서 벨로그에서 많은 도움을 받았는데, 다른 사람도 제 글을 보고 도움이 되었으면 합니다.
감사합니다.
참고
[Spring 레퍼런스] 26장 태스크(Task) 실행과 스케줄링
[스프링 배치] 쿼츠 Quartz 사용하여 배치 스케줄링
Quartz Job Scheduler란?