본문 바로가기
PROGRAM/JAVA / JSP

Spring 배치, 스케쥴러 사용하기 : Quartz, @Scheduled

by ojava 2018. 10. 30.
반응형

Spring 배치, 스케쥴러 사용하기 : Quartz, @Scheduled

 

이놈들 설정하느라고 황금같은 월요일과 화요일을 썼다.

둘을 사용하는 방법을 아주 잘 알게 된 시간이었으니, 각각을 정리하면서 보람차게 마무리하도록 하자.

 

 

 

Quartz

 

처음에는 Quartz를 쓰려고 했다. 설정하다보니 알게 된 내용들을 정리하기도 했더랬다.

결론적으로는 프로젝트 환경과 맞지않는 단점을 하나 발견해서 세팅만 열심히 해놓고 쓰지않았다.

 

2018/10/29 - [PROGRAM/JAVA / JSP] - Spring 3.x + Quartz 연동 시 주의사항

 

 

1) Quartz 사용을 위한 기본 설정

 

pom.xml

 

<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>1.8.6</version>
</dependency>

 

Spring 3.x을 사용하는 경우는 1.8.6을 사용하자.

Spring 4.x의 경우는 2.x 이상으로 세팅해도 상관없는 듯 하다. (물론 안해봐서 확신할 수 없음)

 

 

applicationContext-batch.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"
            xmlns:context="http://www.springframework.org/schema/context"
            xsi:schemaLocation="http://www.springframework.org/schema/beans
                                          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                                          http://www.springframework.org/schema/context
                                          http://www.springframework.org/schema/context/spring-context-3.0.xsd"

 

    <!-- 배치프로그램 (스케줄링) 수행을 위한 설정 부분 #ojava -->

    <!-- 1. 반복적으로 수행될 작업 설정 -->

    <bean id="batchJob" class="org.springframework.scheduling.quartz.JobDetailBean" >
         <property name="jobClass" class="com.ojava.monitoring.common.batchjob.CheckJob" />
     </bean>

 

     <!-- 2. 작업 수행을 얼마 간격으로 할 것인지 설정 (cron) -->

     <bean id="dailyTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean" >
         <property name="jobDetail" ref="batchJob" />
         <property name="cronExpression" value="*/10 * * * * ?" /> <!-- 10초마다 수행된다. -->
     </bean>

 

    <!-- 3. 1,2번에서 설정한 내용 실제 수행되도록 세팅 -->

    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
         <property name="triggers">
             <list>
                 <ref bean="dailyTrigger" />
             </list>
         </property>
     </bean>


 </beans>

 

별도 파일명을 지정했으나, 기존에 있는 applicationContext.xml에 작성해도 관계없다.

만약 신규로 만드는 경우, context-param 설정의, contextConfigLocation 정보를 고쳐줘야 한다.

 

 

CheckJob.java

 

public class CheckJob extends QuartzJobBean () {

 

@Override

protected void executeInternal (JobExecutionContext context) throws JobExecutionException {

 

System.out.println("console check log");

 

}

}

 

 

반복적으로 수행될 프로그램이다. 물론 테스트이므로 콘솔창에 텍스트 찍는 것으로만 구성하였으나, 별도로 선언한 class 파일의 객체와 그 함수를 호출하는 부분으로 구성해도 되고, 해당 파일 내에서 프로그램을 짜도 된다.

 

중요한 내용은 QuartzJobBean을 상속받아서 구현해야 한다는 점이다.

스케쥴이 수행될 때, 기본적으로 executeInteral 함수가 호출된다.

 

 

 

위와 같이 설정하고 나면, 10초마다 콘솔에 텍스트가 찍히는 형태로 스케쥴이 수행된다.

이 방식의 단점은, Spring에서 제공하는 @Autowired를 통한 객체주입의 사용이 불가하다.

(아 물론 AppilcationContext를 불러오도록 설정을 추가적으로 하면 가능하나, 아래에서 소개하는 방법이 더 쉽다.)

 

 

 

@Scheduled

 

위에 저렇게 Quartz 설정을 잘해놓고 결국 변경하게 된 건 아까 발견한 단점때문이다.

기본 설정만으로는 Spring의 최대 이점인 DI (Dependency Injection) 사용이 어렵다.

다시 말해 Controller, Service 등에 선언된 @Autowired 어노테이션으로 주입한 내용을 인식하지 못해서 method call 시 NullPointerException을 마주하게 된다.

 

검색해보니 다들 ApplicationContext를 별도 주입하는 방식으로 해결한 듯 하나, 설정의 문제인지 해결이 어려워서 Quartz를 포기하고 Spring 자체적으로 제공하는 Scheduler를 사용하게 되었다.

 

결론적으로는 설정도 더 간단해졌고 스케줄을 설정하는 방식도 더 스프링다워진 듯 하다.

 

 

applicationContext.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"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:task="http://www.springframework.org/schema/task"
            xsi:schemaLocation="http://www.springframework.org/schema/beans
                                          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                                          http://www.springframework.org/schema/context
                                          http://www.springframework.org/schema/context/spring-context-3.0.xsd

                                          http://www.springframework.org/schema/task
                                          http://www.springframework.org/schema/task/spring-task-3.0.xsd"

 

    <!-- Scheduler 설정 부분 #ojava -->

    <context:annotation-config />

    <task:annotation-driven />

 

 

    <!-- @Scheduled로 선언한 내용 모두를 가져오기 싫은 경우, 선언한 bean만 가져올 수 있다. -->

    <!--

    <bean id="checkController" class="com.ojava.monitoring.batchjob.CheckController" />

 

    <task:annotation-driven scheduler="scheduler" />

    <task:scheduler id="scheduler" pool-size="10" />

 

    <task:scheduled-tasks scheduler="scheduler">

        <task:scheduled ref="checkController" method="checkForBatch" cron="*/30 * * * * ?" />

    </task:scheduler-tasks>

    -->

 

 </beans>

 

 

단, 설정 시 조건이 있다.

해당 설정을 어느 파일에 추가해도 관계없으나, 해당 설정파일에 <context:annotation-config>, <context:component-scan> 설정이 있는 파일에 안에 함께 담는 것이 좋다.

 

★이 부분이 완전 핵심이다. 이거 아니었으면 내일도 설정 변경하고 있었을 듯....★

 

 

설정파일 변경 후, 기존 내용에 문제 생길까봐 별도 파일에 선언해놨는데 이 때도 Scheduled를 잘 인식해서 가져는 왔으나

문제는 @Autowired 된 객체를 인식하지를 못한다는 거다.

 

근데, 어떤 난리를 쳐도 안되던게 저 설정이 같이 있는 파일에 담았더니 아주 잘된다.

 

 

설정 자체도 너무나도 간소하고, 프로그램 내부의 @Autowired도 잘 인식하고.

만약 Spring 프로젝트 내부에서 스케줄링이 필요한 경우가 있다면 이제 Quartz가 아니라 @Scheduled를 사용할 듯 하다.

 

 

 

참고삼아, CheckController 내부의 모습을 간략하게 구현하자면, 다음과 같다.

 

CheckController.java

 

@Controller

@RequestMapping(value="/check")

public class CheckController {

 

@Autowired

private CheckService checkService;

 

@Scheduled(cron="0 30 0/2 * * ?")

public void checkForBatch () {

checkService.callCheck();

}

}

 

 

위와 같은 프로그램을 스케줄링으로 돌리려고 했을 때, @Autowired로 주입된 객체를 인식하지 못하면 스케줄링을 수행하다가 에러가 난다.

 

 

 

미래의 나에게 보내는 정시퇴근을 위한 꿀팁

이 글을 보는 모두들 정시퇴근하세욥 ㅋ_ㅋ

반응형