This commit is contained in:
mindol1004
2024-08-29 17:15:37 +09:00
parent ecbf7f8698
commit 58495710ec
16 changed files with 272 additions and 29 deletions

View File

@@ -0,0 +1,178 @@
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.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>
* 이 클래스는 배치 작업의 기본 설정 및 실행을 관리합니다.
* </p>
*
* @author mindol
* @version 1.0
*/
@Configuration
public abstract class AbstractBatchTask implements ApplicationContextAware {
private final BatchJobInfo batchJobInfo;
private ApplicationContext applicationContext;
private JobRepository jobRepository;
private PlatformTransactionManager transactionManager;
/**
* 기본 생성자입니다.
* <p>
* BatchJobInfo 어노테이션을 찾고, 없으면 예외를 발생시킵니다.
* </p>
*/
protected AbstractBatchTask() {
this.batchJobInfo = AnnotationUtils.findAnnotation(getClass(), BatchJobInfo.class);
if (this.batchJobInfo == null) {
throw new IllegalStateException("BatchJobInfo annotation is missing");
}
}
/**
* ApplicationContext를 설정합니다.
*
* @param applicationContext 설정할 ApplicationContext
* @throws BeansException Beans 예외
*/
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
registerJobBean();
}
/**
* 배치 작업을 Spring의 Bean으로 등록합니다.
*/
private void registerJobBean() {
var beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
var registry = (BeanDefinitionRegistry) beanFactory;
var beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Job.class, this::createJob);
registry.registerBeanDefinition(jobName(), 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;
}
/**
* 배치 작업을 생성합니다.
*
* @return 생성된 Job 객체
* @throws IllegalStateException STEP이 정의되지 않은 경우 예외 발생
*/
private Job createJob() {
List<Step> steps = createSteps();
if (steps.isEmpty()) {
throw new IllegalStateException("No steps defined for job: " + jobName());
}
var jobBuilder = new JobBuilder(jobName())
.incrementer(new RunIdIncrementer())
.repository(jobRepository)
.start(steps.get(0));
for (int i = 1; i < steps.size(); i++) {
jobBuilder = jobBuilder.next(steps.get(i));
}
return jobBuilder.build();
}
/**
* 배치 작업의 STEP을 생성합니다.
*
* @return 생성된 Step 리스트
*/
protected List<Step> createSteps() {
List<Step> steps = new ArrayList<>();
steps.add(addStep(jobName() + "Step", createTasklet()));
return steps;
}
/**
* STEP을 추가합니다.
*
* @param stepName STEP의 이름
* @param tasklet STEP에서 실행할 Tasklet
* @return 생성된 Step 객체
*/
protected Step addStep(String stepName, Tasklet tasklet) {
return new StepBuilder(stepName)
.repository(jobRepository)
.transactionManager(transactionManager)
.tasklet(tasklet)
.build();
}
/**
* 서브클래스에서 구현해야 하는 Tasklet을 생성합니다.
*
* @return 생성된 Tasklet 객체
*/
protected abstract Tasklet createTasklet();
/**
* 배치 작업 그룹을 반환합니다.
*
* @return 배치 작업 그룹 이름
*/
public String group() {
return batchJobInfo.group();
}
/**
* 배치 작업 이름을 반환합니다.
*
* @return 배치 작업 이름
*/
public String jobName() {
return batchJobInfo.jobName();
}
/**
* cron 표현식을 반환합니다.
*
* @return cron 표현식
*/
public String cronExpression() {
return batchJobInfo.cronExpression();
}
}