+ * 이 클래스는 배치 작업의 기본 설정 및 실행을 관리합니다. + *
+ * + * @author mindol + * @version 1.0 + */ +@Configuration +public abstract class AbstractBatchJob implements ApplicationContextAware { + + private final BatchJobInfo batchJobInfo; + private ApplicationContext applicationContext; + private JobRepository jobRepository; + private PlatformTransactionManager transactionManager; + + /** + * 기본 생성자입니다. + *+ * BatchJobInfo 어노테이션을 찾고, 없으면 예외를 발생시킵니다. + *
+ */ + protected AbstractBatchJob() { + 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+ * 이 클래스는 Spring Batch의 배치 처리 기능을 활성화하고, + * JobRegistryBeanPostProcessor를 설정하여 배치 작업을 등록합니다. + *
+ * + *+ * {@code @EnableBatchProcessing} 어노테이션은 Spring Batch의 배치 처리 기능을 활성화합니다. + * 이 어노테이션을 사용하면, 배치 작업을 정의하고 실행하는 데 필요한 기본 구성 요소들이 자동으로 설정됩니다. + *
+ */ +@Configuration +@EnableBatchProcessing public class BatchConfig { - + + /** + * JobRegistryBeanPostProcessor를 생성합니다. + *+ * 이 메서드는 JobRegistry를 사용하여 배치 작업을 등록하는 + * JobRegistryBeanPostProcessor를 반환합니다. + *
+ * + * @param jobRegistry JobRegistry 객체 + * @return 설정된 JobRegistryBeanPostProcessor 객체 + */ + @Bean + JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) { + final var processor = new JobRegistryBeanPostProcessor(); + processor.setJobRegistry(jobRegistry); + return processor; + } } diff --git a/batch-quartz/src/main/java/com/spring/infra/batch/BatchJobInfo.java b/batch-quartz/src/main/java/com/spring/infra/batch/BatchJobInfo.java new file mode 100644 index 0000000..93bf674 --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/infra/batch/BatchJobInfo.java @@ -0,0 +1,46 @@ +package com.spring.infra.batch; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 배치 작업에 대한 메타데이터를 정의하는 어노테이션입니다. + *+ * 이 어노테이션은 배치 작업의 그룹, 이름 및 cron 표현식을 설정하는 데 사용됩니다. + *
+ */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface BatchJobInfo { + /** + * 배치 작업의 그룹을 정의합니다. + *+ * 기본값은 "DEFAULT"입니다. + *
+ * + * @return 배치 작업 그룹 이름 + */ + String group() default "DEFAULT"; + + /** + * 배치 작업의 이름을 정의합니다. + *+ * 이 값은 배치 작업을 식별하는 데 사용됩니다. + *
+ * + * @return 배치 작업 이름 + */ + String jobName() default ""; + + /** + * 배치 작업의 cron 표현식을 정의합니다. + *+ * 이 표현식은 배치 작업의 실행 주기를 설정하는 데 사용됩니다. + *
+ * + * @return cron 표현식 + */ + String cronExpression() default ""; +} diff --git a/batch-quartz/src/main/java/com/spring/infra/db/PrimaryDataSourceConfig.java b/batch-quartz/src/main/java/com/spring/infra/db/PrimaryDataSourceConfig.java index d44e479..dab71c9 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/PrimaryDataSourceConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/PrimaryDataSourceConfig.java @@ -8,6 +8,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; import com.zaxxer.hikari.HikariDataSource; @@ -57,7 +58,9 @@ public class PrimaryDataSourceConfig { @Primary @Bean(name = DATASOURCE) DataSource dataSource(@Qualifier(DATASOURCE_PROPERTIES) DataSourceProperties properties) { - return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); + return new LazyConnectionDataSourceProxy( // 커넥션이 필요한 경우에만 datasource 에서 connection 을 반환 + properties.initializeDataSourceBuilder().type(HikariDataSource.class).build() + ); } } diff --git a/batch-quartz/src/main/java/com/spring/infra/db/SecondaryDataSourceConfig.java b/batch-quartz/src/main/java/com/spring/infra/db/SecondaryDataSourceConfig.java index 808948f..c6163f9 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/SecondaryDataSourceConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/SecondaryDataSourceConfig.java @@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; import com.zaxxer.hikari.HikariDataSource; @@ -54,7 +55,9 @@ public class SecondaryDataSourceConfig { */ @Bean(name = DATASOURCE) DataSource dataSource(@Qualifier(DATASOURCE_PROPERTIES) DataSourceProperties properties) { - return properties.initializeDataSourceBuilder().type(HikariDataSource.class).build(); + return new LazyConnectionDataSourceProxy( // 커넥션이 필요한 경우에만 datasource 에서 connection 을 반환 + properties.initializeDataSourceBuilder().type(HikariDataSource.class).build() + ); } } diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/PrimaryMybatisConfig.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/PrimaryMybatisConfig.java index a8e5262..814bd87 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/PrimaryMybatisConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/PrimaryMybatisConfig.java @@ -38,7 +38,7 @@ import com.spring.infra.db.orm.mybatis.annotation.PrimaryMapper; public class PrimaryMybatisConfig { private static final String BASE_PACKAGE = "com.spring.domain.*.mapper"; - private static final String ALIASES_PACKAGE = "com.spring.domain.*.dto"; + private static final String ALIASES_PACKAGE = "com.spring.domain.*.dto,com.spring.domain.*.entity"; private static final String MAPPER_RESOURCES = "classpath:mapper/**/*.xml"; private static final String SESSION_FACTORY = "primarySqlSessionFactory"; diff --git a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/SecondaryMybatisConfig.java b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/SecondaryMybatisConfig.java index d064fdd..c16cac2 100644 --- a/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/SecondaryMybatisConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/db/orm/mybatis/SecondaryMybatisConfig.java @@ -37,7 +37,7 @@ import com.spring.infra.db.orm.mybatis.annotation.SecondaryMapper; public class SecondaryMybatisConfig { private static final String BASE_PACKAGE = "com.spring.domain.*.mapper"; - private static final String ALIASES_PACKAGE = "com.spring.domain.*.dto"; + private static final String ALIASES_PACKAGE = "com.spring.domain.*.dto,com.spring.domain.*.entity"; private static final String MAPPER_RESOURCES = "classpath:mapper/**/*.xml"; private static final String SESSION_FACTORY = "secondarySqlSessionFactory"; diff --git a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJob.java b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJob.java index 2ba225e..e5b4a49 100644 --- a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJob.java +++ b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJob.java @@ -43,12 +43,12 @@ public @interface QuartzJob { * * @return 작업의 이름 */ - String name(); + String jobName() default ""; /** * 작업의 실행 주기를 Cron 표현식으로 지정합니다. * * @return Cron 표현식 */ - String cronExpression(); + String cronExpression() default ""; } diff --git a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobRegistrar.java b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobRegistrar.java index c351a9c..a02f725 100644 --- a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobRegistrar.java +++ b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzJobRegistrar.java @@ -1,8 +1,10 @@ package com.spring.infra.quartz; import java.lang.reflect.Method; +import java.util.Map; import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; import org.quartz.JobBuilder; import org.quartz.JobDataMap; import org.quartz.JobDetail; @@ -17,18 +19,18 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; +import com.spring.infra.batch.AbstractBatchJob; + import lombok.RequiredArgsConstructor; /** * Quartz 작업 등록기 클래스입니다. * - *이 클래스는 애플리케이션 컨텍스트가 리프레시될 때 실행되며, - * {@link QuartzJob} 어노테이션이 붙은 모든 메소드를 찾아 Quartz 스케줄러에 등록합니다.
- * *주요 기능:
*