237 lines
8.1 KiB
Java
237 lines
8.1 KiB
Java
package com.spring.infra.batch;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import org.springframework.batch.core.Job;
|
|
import org.springframework.batch.core.Step;
|
|
import org.springframework.batch.core.job.builder.JobBuilder;
|
|
import org.springframework.batch.core.launch.support.RunIdIncrementer;
|
|
import org.springframework.batch.core.repository.JobRepository;
|
|
import org.springframework.batch.core.step.builder.StepBuilder;
|
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
|
import org.springframework.beans.BeansException;
|
|
import org.springframework.beans.factory.InitializingBean;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.ApplicationContextAware;
|
|
import org.springframework.context.ConfigurableApplicationContext;
|
|
import org.springframework.context.annotation.Configuration;
|
|
import org.springframework.core.annotation.AnnotationUtils;
|
|
import org.springframework.lang.NonNull;
|
|
import org.springframework.transaction.PlatformTransactionManager;
|
|
|
|
/**
|
|
* 배치 작업을 정의하는 추상 클래스입니다.
|
|
*
|
|
* <p>이 클래스는 배치 작업의 기본 설정 및 실행을 관리하며,
|
|
* 배치 작업의 메타데이터를 초기화하고, JobRepository에 등록하는 기능을 제공합니다.</p>
|
|
*
|
|
* <p>구현 클래스는 이 클래스를 상속받아 특정 배치 작업의 로직을 정의해야 합니다.</p>
|
|
*
|
|
* @author mindol
|
|
* @version 1.0
|
|
*/
|
|
@Configuration
|
|
public abstract class AbstractBatchTask implements AbstractBatch, ApplicationContextAware, InitializingBean {
|
|
|
|
/**
|
|
* 배치 작업 정보.
|
|
*
|
|
* <p>BatchJobInfo 어노테이션을 통해 정의된 배치 작업의 메타데이터를 포함합니다.</p>
|
|
*/
|
|
private final BatchJobInfo batchJobInfo;
|
|
|
|
/**
|
|
* 배치 작업 정보 서비스.
|
|
*
|
|
* <p>배치 작업 정보를 관리하는 서비스 객체입니다.</p>
|
|
*/
|
|
private BatchJobInfoService batchJobInfoService;
|
|
|
|
/**
|
|
* 배치 작업 정보 데이터.
|
|
*
|
|
* <p>배치 작업의 실행에 필요한 데이터를 포함합니다.</p>
|
|
*/
|
|
private BatchJobInfoData batchJobInfoData;
|
|
|
|
/**
|
|
* 애플리케이션 컨텍스트.
|
|
*
|
|
* <p>Spring의 ApplicationContext를 통해 필요한 빈을 가져옵니다.</p>
|
|
*/
|
|
private ApplicationContext applicationContext;
|
|
|
|
/**
|
|
* JobRepository.
|
|
*
|
|
* <p>배치 작업의 상태를 관리하는 리포지토리입니다.</p>
|
|
*/
|
|
private JobRepository jobRepository;
|
|
|
|
/**
|
|
* 트랜잭션 매니저.
|
|
*
|
|
* <p>배치 작업의 트랜잭션 관리를 담당합니다.</p>
|
|
*/
|
|
private PlatformTransactionManager transactionManager;
|
|
|
|
private BatchExceptionListener batchExceptionListener;
|
|
|
|
/**
|
|
* 기본 생성자입니다.
|
|
*
|
|
* <p>BatchJobInfo 어노테이션을 찾고, 없으면 예외를 발생시킵니다.</p>
|
|
*/
|
|
protected AbstractBatchTask() {
|
|
this.batchJobInfo = AnnotationUtils.findAnnotation(getClass(), BatchJobInfo.class);
|
|
if (this.batchJobInfo == null) {
|
|
throw new IllegalStateException("BatchJobInfo annotation is missing");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 애플리케이션 컨텍스트를 설정합니다.
|
|
*
|
|
* @param applicationContext 설정할 ApplicationContext
|
|
* @throws BeansException 빈 설정 중 발생할 수 있는 예외
|
|
*/
|
|
@Override
|
|
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
|
|
this.applicationContext = applicationContext;
|
|
this.batchJobInfoService = applicationContext.getBean(BatchJobInfoService.class);
|
|
}
|
|
|
|
/**
|
|
* 빈의 속성이 설정된 후 호출됩니다.
|
|
*
|
|
* <p>배치 작업 정보 초기화 및 배치 작업 등록을 수행합니다.</p>
|
|
*
|
|
* @throws Exception 초기화 중 발생할 수 있는 예외
|
|
*/
|
|
@Override
|
|
public void afterPropertiesSet() throws Exception {
|
|
initializeBatchJobInfo();
|
|
registerJobBean();
|
|
}
|
|
|
|
/**
|
|
* 배치 작업 정보를 초기화합니다.
|
|
*
|
|
* <p>배치 작업의 메타데이터를 로드하여 설정합니다.</p>
|
|
*/
|
|
@Override
|
|
public void initializeBatchJobInfo() {
|
|
String beanName = applicationContext.getBeanNamesForType(this.getClass())[0];
|
|
this.batchJobInfoData = batchJobInfoService.getBatchJobInfo(removeScopedTargetPrefix(beanName));
|
|
}
|
|
|
|
/**
|
|
* 배치 작업을 Spring의 Bean으로 등록합니다.
|
|
*
|
|
* <p>JobRepository에 배치 작업을 등록하여 실행할 수 있도록 합니다.</p>
|
|
*/
|
|
@Override
|
|
public void registerJobBean() {
|
|
var beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
|
|
var registry = (BeanDefinitionRegistry) beanFactory;
|
|
String jobBeanName = batchJobInfoData.getJobName();
|
|
if (!registry.containsBeanDefinition(jobBeanName)) {
|
|
var beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Job.class, this::createJob);
|
|
registry.registerBeanDefinition(jobBeanName, beanDefinitionBuilder.getBeanDefinition());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* JobRepository를 설정합니다.
|
|
*
|
|
* @param jobRepository 설정할 JobRepository
|
|
*/
|
|
@Autowired
|
|
public void setJobRepository(JobRepository jobRepository) {
|
|
this.jobRepository = jobRepository;
|
|
}
|
|
|
|
/**
|
|
* PlatformTransactionManager를 설정합니다.
|
|
*
|
|
* @param transactionManager 설정할 PlatformTransactionManager
|
|
*/
|
|
@Autowired
|
|
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
|
this.transactionManager = transactionManager;
|
|
}
|
|
|
|
@Autowired
|
|
public void setBatchExceptionListener(BatchExceptionListener batchExceptionListener) {
|
|
this.batchExceptionListener = batchExceptionListener;
|
|
}
|
|
|
|
/**
|
|
* 배치 작업을 생성합니다.
|
|
*
|
|
* <p>구현 클래스에서 정의해야 하는 추상 메서드입니다.</p>
|
|
*
|
|
* @return 생성된 Job 객체
|
|
* @throws IllegalStateException STEP이 정의되지 않은 경우 예외 발생
|
|
*/
|
|
@Override
|
|
public Job createJob() {
|
|
List<Step> steps = createSteps();
|
|
if (steps.isEmpty()) {
|
|
throw new IllegalStateException("No steps defined for job: " + batchJobInfoData.getJobName());
|
|
}
|
|
var jobBuilder = new JobBuilder(batchJobInfoData.getJobName())
|
|
.incrementer(new RunIdIncrementer())
|
|
.repository(jobRepository)
|
|
.listener(batchExceptionListener)
|
|
.start(steps.get(0));
|
|
for (int i = 1; i < steps.size(); i++) {
|
|
jobBuilder = jobBuilder.next(steps.get(i));
|
|
}
|
|
return jobBuilder.build();
|
|
}
|
|
|
|
/**
|
|
* 배치 작업의 STEP을 생성합니다.
|
|
*
|
|
* <p>기본적으로 하나의 STEP을 생성하여 반환합니다.</p>
|
|
*
|
|
* @return 생성된 Step 리스트
|
|
*/
|
|
protected List<Step> createSteps() {
|
|
List<Step> steps = new ArrayList<>();
|
|
steps.add(addStep(batchJobInfoData.getJobName() + "Step", createTasklet()));
|
|
return steps;
|
|
}
|
|
|
|
/**
|
|
* STEP을 추가합니다.
|
|
*
|
|
* <p>주어진 이름과 Tasklet을 사용하여 Step 객체를 생성합니다.</p>
|
|
*
|
|
* @param stepName STEP의 이름
|
|
* @param tasklet STEP에서 실행할 Tasklet
|
|
* @return 생성된 Step 객체
|
|
*/
|
|
protected Step addStep(String stepName, Tasklet tasklet) {
|
|
return new StepBuilder(stepName)
|
|
.repository(jobRepository)
|
|
.transactionManager(transactionManager)
|
|
.listener(batchExceptionListener)
|
|
.tasklet(tasklet)
|
|
.build();
|
|
}
|
|
|
|
/**
|
|
* 서브클래스에서 구현해야 하는 Tasklet을 생성합니다.
|
|
*
|
|
* @return 생성된 Tasklet 객체
|
|
*/
|
|
protected abstract Tasklet createTasklet();
|
|
|
|
}
|