[Spring] Spring Batch + Quartz Scheduler

John·2022년 5월 12일
1

개발 메모🌷

목록 보기
3/13
post-thumbnail

기존 프로젝트에 배치 기능을 추가한 적이 있습니다.
선배님께서 Quartz Scheduler를 공부해보고 개발에 적용해보라는 말씀에 Quartz Scheduler를 공부하고 개발 및 배포까지 수행했습니다.

관련해서 다시 공부하기 위해 적는 글입니다.


Quartz Job Scheduler

  • 오픈소스 스케줄러
  • 쿼츠는 자바 환경의 규모와 상관없이 사용이 가능하고 잡 실행에 유용한 스프링 부트 지원과 같이 오래전부터 스프링 연동을 지원

1. dependency 추가

1-1

1-2

<!-- 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를 다운받아 추가합니다.


2. Quartz Scheduler

Quartz는 일반 클래스로 동작하기 때문에 Spring DI를 사용할 수 없습니다.
따라서 @Autowired를 통해 Service를 호출하여도 null이 출력되며 동작하지 않습니다.

이를 해결하기 위해 Job 과 Task를 분리하여 코드를 작성해보겠습니다.


2-1 Job 클래스 작성

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();
	}

}

수정자 주입 방식 (Setter Injection)

@Autowired
public void setTestBatchTask(TestBatchTask testBatchTask) {
		this.testBatchTask = testBatchTask;
}

Task를 주입하여 Spring DI 컨테이너가 사용할 수 있는 실제 작업이 일어나는 객체입니다.


QuartzJobBean의 executeInternal 메서드 오버라이드

@Override
protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
		testBatchTask.task();
}

스케줄러에 의해 Job 클래스가 호출되었을 때 해당 executeInternal 메소드를 실행합니다.


2-2 Task 클래스 작성

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());
		}
	}

}

TestBatchTask

빈(Bean)으로 등록된 TestBatchTask 객체는 의존성 주입을 통해 Service 객체 사용이 가능합니다.
실제 스케줄링이 일어났을 때(executeInternal 참조) task() 메소드가 실행됩니다.


2-3 batch-context.xml 작성

<?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>

빈(Bean) 등록

<!-- Task bean 생성 -->
<bean id="testBatchTask" class="com.study.common.batch.TestBatchTask" />

Spring IoC 컨테이너가 관리할 수 있도록 TestBatchTask 객체를 빈(Bean)으로 등록


JobDetailFactoryBean

<!-- 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 작업을 구현하는 방법에 대해 공부하고 작성한 글입니다.

실제 작업에서 변경 또는 추가한 내용

  • cron식은 DB에서 관리
  • AbstactBatch라는 abstract class를 작성하여 Job에서 상속받아 사용(로그 등)

Quartz를 공부하면서 벨로그에서 많은 도움을 받았는데, 다른 사람도 제 글을 보고 도움이 되었으면 합니다.

감사합니다.


참고
[Spring 레퍼런스] 26장 태스크(Task) 실행과 스케줄링
[스프링 배치] 쿼츠 Quartz 사용하여 배치 스케줄링
Quartz Job Scheduler란?

profile
기록을 습관으로

0개의 댓글