diff --git a/batch-quartz/pom.xml b/batch-quartz/pom.xml index 6750a24..ff1d3aa 100644 --- a/batch-quartz/pom.xml +++ b/batch-quartz/pom.xml @@ -13,19 +13,7 @@ 0.0.1-SNAPSHOT batch-quartz Demo project for Spring Boot - - - - - - - - - - - - - + 11 @@ -60,6 +48,10 @@ org.springframework.boot spring-boot-starter-batch + + org.springframework.boot + spring-boot-starter-quartz + org.springframework.boot spring-boot-starter-data-jpa @@ -67,11 +59,7 @@ org.mybatis.spring.boot mybatis-spring-boot-starter - 3.0.3 - - - org.springframework.boot - spring-boot-starter-quartz + 2.2.0 io.jsonwebtoken diff --git a/batch-quartz/src/main/java/com/spring/domain/email/batch/EmailSendBatch.java b/batch-quartz/src/main/java/com/spring/domain/email/batch/EmailSendBatch.java index 98175ab..079e688 100644 --- a/batch-quartz/src/main/java/com/spring/domain/email/batch/EmailSendBatch.java +++ b/batch-quartz/src/main/java/com/spring/domain/email/batch/EmailSendBatch.java @@ -1,44 +1,45 @@ package com.spring.domain.email.batch; -import org.springframework.batch.core.Job; +import java.util.List; + import org.springframework.batch.core.Step; -import org.springframework.batch.core.job.builder.JobBuilder; -import org.springframework.batch.core.step.builder.StepBuilder; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; -import com.spring.infra.quartz.QuartzJob; +import com.spring.infra.batch.AbstractBatchJob; +import com.spring.infra.batch.BatchJobInfo; import lombok.extern.slf4j.Slf4j; @Slf4j -@Configuration -public class EmailSendBatch { +@Component +@BatchJobInfo( + group = "EMAIL", + jobName = "emailSendJob", + cronExpression = "*/5 * * * * ?" +) +public class EmailSendBatch extends AbstractBatchJob { - @QuartzJob(group = "EMAIL", name = "emailSendJob", cronExpression = "*/3 * * * * ?") - // @JobScope - @Bean(name = "emailSendJob") - Job emailSendJob(Step emailSendStep) { - log.info(">>> emailSendJob"); - return new JobBuilder("emailSendJob") - .start(emailSendStep) - .build(); + @Override + protected List createSteps() { + return List.of( + addStep("emailSendJobStep1111", createTasklet()), + addStep("emailSendJobStep2222", createSendTasklet()) + ); } - // @StepScope - @Bean("emailSendStep") - Step emailSendStep(Tasklet emailSendTasklet) { - log.info(">>> emailSendStep"); - return new StepBuilder("emailSendStep") - .tasklet(emailSendTasklet).build(); - } - - @Bean - Tasklet emailSendTasklet() { + @Override + protected Tasklet createTasklet() { return ((contribution, chunkContext) -> { - log.info(">>>>> emailSendTasklet"); + log.info(">>>>> emailSendTasklet1111111"); + return RepeatStatus.FINISHED; + }); + } + + private Tasklet createSendTasklet() { + return ((contribution, chunkContext) -> { + log.info(">>>>> emailSendTasklet2222222"); return RepeatStatus.FINISHED; }); } diff --git a/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatch.java b/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatch.java new file mode 100644 index 0000000..9c010c2 --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/domain/post/batch/PostCreateBatch.java @@ -0,0 +1,52 @@ +package com.spring.domain.post.batch; + +import java.util.List; + +import org.springframework.batch.core.step.tasklet.Tasklet; +import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; +import org.springframework.transaction.PlatformTransactionManager; + +import com.spring.domain.post.entity.Post; +import com.spring.domain.post.mapper.PostMapper; +import com.spring.infra.batch.AbstractBatchJob; +import com.spring.infra.batch.BatchJobInfo; +import com.spring.infra.db.orm.jpa.SecondaryJpaConfig; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@BatchJobInfo( + group = "POST", + jobName = "postCreateJob", + cronExpression = "*/5 * * * * ?" +) +@RequiredArgsConstructor +public class PostCreateBatch extends AbstractBatchJob { + + private final PostMapper postMapper; + + @Autowired + @Override + public void setTransactionManager( + @Qualifier(SecondaryJpaConfig.TRANSACTION_MANAGER) PlatformTransactionManager transactionManager + ) { + super.setTransactionManager(transactionManager); + } + + @Override + protected Tasklet createTasklet() { + return ((contribution, chunkContext) -> { + postMapper.save(Post.builder().title("testTitle").content("testPost").build()); + log.info(">>>>> PostCreateBatchTasklet333333333"); + List list = postMapper.findAll(); + list.forEach(item -> log.info(item.getContent())); + return RepeatStatus.FINISHED; + }); + } + +} diff --git a/batch-quartz/src/main/java/com/spring/domain/post/entity/Post.java b/batch-quartz/src/main/java/com/spring/domain/post/entity/Post.java index f930ccb..719a20a 100644 --- a/batch-quartz/src/main/java/com/spring/domain/post/entity/Post.java +++ b/batch-quartz/src/main/java/com/spring/domain/post/entity/Post.java @@ -10,12 +10,14 @@ import javax.persistence.Table; import com.spring.infra.db.SecondaryDataSourceConfig; import com.spring.infra.db.orm.jpa.annotation.DatabaseSelector; +import lombok.Builder; import lombok.Getter; @DatabaseSelector(SecondaryDataSourceConfig.DATABASE) @Entity @Table(name = "APP_POST") @Getter +@Builder public class Post { @Id diff --git a/batch-quartz/src/main/java/com/spring/domain/post/mapper/PostMapper.java b/batch-quartz/src/main/java/com/spring/domain/post/mapper/PostMapper.java index f0bc30f..40699ca 100644 --- a/batch-quartz/src/main/java/com/spring/domain/post/mapper/PostMapper.java +++ b/batch-quartz/src/main/java/com/spring/domain/post/mapper/PostMapper.java @@ -1,8 +1,14 @@ package com.spring.domain.post.mapper; +import java.util.List; + +import org.apache.ibatis.annotations.Param; + +import com.spring.domain.post.entity.Post; import com.spring.infra.db.orm.mybatis.annotation.SecondaryMapper; @SecondaryMapper public interface PostMapper { - + List findAll(); + void save(@Param("post") Post post); } diff --git a/batch-quartz/src/main/java/com/spring/infra/batch/AbstractBatchJob.java b/batch-quartz/src/main/java/com/spring/infra/batch/AbstractBatchJob.java new file mode 100644 index 0000000..3115651 --- /dev/null +++ b/batch-quartz/src/main/java/com/spring/infra/batch/AbstractBatchJob.java @@ -0,0 +1,174 @@ +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.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; + +/** + * 배치 작업을 정의하는 추상 클래스입니다. + *

+ * 이 클래스는 배치 작업의 기본 설정 및 실행을 관리합니다. + *

+ * + * @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 steps = createSteps(); + if (steps.isEmpty()) { + throw new IllegalStateException("No steps defined for job: " + jobName()); + } + var jobBuilder = new JobBuilder(jobName()).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 createSteps() { + List 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(); + } + +} diff --git a/batch-quartz/src/main/java/com/spring/infra/batch/BatchConfig.java b/batch-quartz/src/main/java/com/spring/infra/batch/BatchConfig.java index 9ee0e0b..cbc9958 100644 --- a/batch-quartz/src/main/java/com/spring/infra/batch/BatchConfig.java +++ b/batch-quartz/src/main/java/com/spring/infra/batch/BatchConfig.java @@ -1,6 +1,42 @@ package com.spring.infra.batch; +import org.springframework.batch.core.configuration.JobRegistry; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 배치 작업을 위한 설정 클래스입니다. + *

+ * 이 클래스는 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 스케줄러에 등록합니다.

- * *

주요 기능:

*
    *
  • 애플리케이션 컨텍스트 내의 모든 빈을 검사
  • *
  • QuartzJob 어노테이션이 붙은 메소드 식별
  • + *
  • AbstractBatchJob을 상속받은 클래스 식별
  • *
  • 식별된 메소드를 Quartz 작업으로 등록
  • *
  • 각 작업에 대한 JobDetail 및 Trigger 생성
  • *
@@ -38,9 +40,6 @@ import lombok.RequiredArgsConstructor; * * @author mindol * @version 1.0 - * @see QuartzJob - * @see ApplicationListener - * @see ContextRefreshedEvent */ @Component @RequiredArgsConstructor @@ -50,53 +49,50 @@ public class QuartzJobRegistrar implements ApplicationListener beanClass = bean.getClass(); for (Method method : beanClass.getDeclaredMethods()) { QuartzJob quartzJobAnnotation = AnnotationUtils.findAnnotation(method, QuartzJob.class); if (quartzJobAnnotation != null) { - registerQuartzJob(quartzJobAnnotation, method.getName()); + try { + JobDetail jobDetail = createJobDetail(quartzJobAnnotation); + Trigger trigger = createTrigger(quartzJobAnnotation, jobDetail); + scheduler.scheduleJob(jobDetail, trigger); + } catch (SchedulerException e) { + throw new IllegalStateException("Error scheduling Quartz job", e); + } } } } } - /** - * QuartzJob 어노테이션 정보를 바탕으로 Quartz 작업을 등록합니다. - * - * @param quartzJobAnnotation QuartzJob 어노테이션 객체 - * @param jobName 등록할 작업의 이름 - */ - private void registerQuartzJob(QuartzJob quartzJobAnnotation, String jobName) { - try { - JobDetail jobDetail = createJobDetail(quartzJobAnnotation, jobName); - Trigger trigger = createTrigger(quartzJobAnnotation, jobDetail); - scheduler.scheduleJob(jobDetail, trigger); - } catch (SchedulerException e) { - throw new IllegalStateException("Error scheduling Quartz job", e); - } - } - /** * QuartzJob 어노테이션 정보를 바탕으로 JobDetail 객체를 생성합니다. * * @param quartzJobAnnotation QuartzJob 어노테이션 객체 - * @param jobName 작업의 이름 * @return 생성된 JobDetail 객체 */ - private JobDetail createJobDetail(QuartzJob quartzJobAnnotation, String jobName) { + private JobDetail createJobDetail(QuartzJob quartzJobAnnotation) { JobDataMap jobDataMap = new JobDataMap(); - jobDataMap.put("jobName", jobName); + jobDataMap.put("jobName", quartzJobAnnotation.jobName()); return JobBuilder.newJob(QuartzJobLauncher.class) - .withIdentity(quartzJobAnnotation.name(), quartzJobAnnotation.group()) + .withIdentity(quartzJobAnnotation.jobName(), quartzJobAnnotation.group()) .setJobData(jobDataMap) .storeDurably() .build(); @@ -112,9 +108,35 @@ public class QuartzJobRegistrar implements ApplicationListener batchJobs = applicationContext.getBeansOfType(AbstractBatchJob.class); + for (AbstractBatchJob batchJob : batchJobs.values()) { + JobDataMap jobDataMap = new JobDataMap(); + jobDataMap.put("jobName", batchJob.jobName()); + JobDetail jobDetail = JobBuilder.newJob(QuartzJobLauncher.class) + .withIdentity(batchJob.jobName(), batchJob.group()) + .setJobData(jobDataMap) + .storeDurably() + .build(); + CronTrigger trigger = TriggerBuilder.newTrigger() + .forJob(jobDetail) + .withIdentity(batchJob.jobName() + "Trigger", batchJob.group()) + .withSchedule(CronScheduleBuilder.cronSchedule(batchJob.cronExpression())) + .build(); + try { + scheduler.scheduleJob(jobDetail, trigger); + } catch (SchedulerException e) { + throw new IllegalStateException("Error scheduling AbstractBatchJob: " + batchJob.jobName(), e); + } + } + } + } diff --git a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzProperties.java b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzProperties.java index 96a9534..d9700ec 100644 --- a/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzProperties.java +++ b/batch-quartz/src/main/java/com/spring/infra/quartz/QuartzProperties.java @@ -6,6 +6,7 @@ import java.util.Arrays; import java.util.Properties; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -29,6 +30,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j @Getter @RequiredArgsConstructor +@ConstructorBinding @ConfigurationProperties(prefix = "spring.quartz.properties.org.quartz") public class QuartzProperties { diff --git a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtProperties.java b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtProperties.java index b1f66b5..3ced20a 100644 --- a/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtProperties.java +++ b/batch-quartz/src/main/java/com/spring/infra/security/jwt/JwtProperties.java @@ -1,6 +1,7 @@ package com.spring.infra.security.jwt; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -12,6 +13,7 @@ import lombok.RequiredArgsConstructor; * @version 1.0 */ @Getter +@ConstructorBinding @ConfigurationProperties(prefix = "jwt") @RequiredArgsConstructor public final class JwtProperties { diff --git a/batch-quartz/src/main/resources/mapper/post/PostMapper.xml b/batch-quartz/src/main/resources/mapper/post/PostMapper.xml new file mode 100644 index 0000000..4779ce8 --- /dev/null +++ b/batch-quartz/src/main/resources/mapper/post/PostMapper.xml @@ -0,0 +1,15 @@ + + + + + + + insert + into app_post (content, title) + values (#{post.content}, #{post.title}) + + + + \ No newline at end of file diff --git a/batch-quartz/src/main/resources/mapper/user/AppUserMapper.xml b/batch-quartz/src/main/resources/mapper/user/AppUserMapper.xml index 1b6c79b..90434ff 100644 --- a/batch-quartz/src/main/resources/mapper/user/AppUserMapper.xml +++ b/batch-quartz/src/main/resources/mapper/user/AppUserMapper.xml @@ -3,8 +3,7 @@ - + select * from app_user \ No newline at end of file