commit
This commit is contained in:
@@ -13,19 +13,7 @@
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>batch-quartz</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
<url/>
|
||||
<licenses>
|
||||
<license/>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer/>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<java.version>11</java.version>
|
||||
</properties>
|
||||
@@ -60,6 +48,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-batch</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
@@ -67,11 +59,7 @@
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||
<version>2.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
|
||||
@@ -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<Step> 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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<Post> list = postMapper.findAll();
|
||||
list.forEach(item -> log.info(item.getContent()));
|
||||
return RepeatStatus.FINISHED;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<Post> findAll();
|
||||
void save(@Param("post") Post post);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 배치 작업을 정의하는 추상 클래스입니다.
|
||||
* <p>
|
||||
* 이 클래스는 배치 작업의 기본 설정 및 실행을 관리합니다.
|
||||
* </p>
|
||||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* 기본 생성자입니다.
|
||||
* <p>
|
||||
* BatchJobInfo 어노테이션을 찾고, 없으면 예외를 발생시킵니다.
|
||||
* </p>
|
||||
*/
|
||||
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<Step> 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<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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 배치 작업을 위한 설정 클래스입니다.
|
||||
* <p>
|
||||
* 이 클래스는 Spring Batch의 배치 처리 기능을 활성화하고,
|
||||
* JobRegistryBeanPostProcessor를 설정하여 배치 작업을 등록합니다.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* {@code @EnableBatchProcessing} 어노테이션은 Spring Batch의 배치 처리 기능을 활성화합니다.
|
||||
* 이 어노테이션을 사용하면, 배치 작업을 정의하고 실행하는 데 필요한 기본 구성 요소들이 자동으로 설정됩니다.
|
||||
* </p>
|
||||
*/
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public class BatchConfig {
|
||||
|
||||
|
||||
/**
|
||||
* JobRegistryBeanPostProcessor를 생성합니다.
|
||||
* <p>
|
||||
* 이 메서드는 JobRegistry를 사용하여 배치 작업을 등록하는
|
||||
* JobRegistryBeanPostProcessor를 반환합니다.
|
||||
* </p>
|
||||
*
|
||||
* @param jobRegistry JobRegistry 객체
|
||||
* @return 설정된 JobRegistryBeanPostProcessor 객체
|
||||
*/
|
||||
@Bean
|
||||
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
|
||||
final var processor = new JobRegistryBeanPostProcessor();
|
||||
processor.setJobRegistry(jobRegistry);
|
||||
return processor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 배치 작업에 대한 메타데이터를 정의하는 어노테이션입니다.
|
||||
* <p>
|
||||
* 이 어노테이션은 배치 작업의 그룹, 이름 및 cron 표현식을 설정하는 데 사용됩니다.
|
||||
* </p>
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface BatchJobInfo {
|
||||
/**
|
||||
* 배치 작업의 그룹을 정의합니다.
|
||||
* <p>
|
||||
* 기본값은 "DEFAULT"입니다.
|
||||
* </p>
|
||||
*
|
||||
* @return 배치 작업 그룹 이름
|
||||
*/
|
||||
String group() default "DEFAULT";
|
||||
|
||||
/**
|
||||
* 배치 작업의 이름을 정의합니다.
|
||||
* <p>
|
||||
* 이 값은 배치 작업을 식별하는 데 사용됩니다.
|
||||
* </p>
|
||||
*
|
||||
* @return 배치 작업 이름
|
||||
*/
|
||||
String jobName() default "";
|
||||
|
||||
/**
|
||||
* 배치 작업의 cron 표현식을 정의합니다.
|
||||
* <p>
|
||||
* 이 표현식은 배치 작업의 실행 주기를 설정하는 데 사용됩니다.
|
||||
* </p>
|
||||
*
|
||||
* @return cron 표현식
|
||||
*/
|
||||
String cronExpression() default "";
|
||||
}
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -43,12 +43,12 @@ public @interface QuartzJob {
|
||||
*
|
||||
* @return 작업의 이름
|
||||
*/
|
||||
String name();
|
||||
String jobName() default "";
|
||||
|
||||
/**
|
||||
* 작업의 실행 주기를 Cron 표현식으로 지정합니다.
|
||||
*
|
||||
* @return Cron 표현식
|
||||
*/
|
||||
String cronExpression();
|
||||
String cronExpression() default "";
|
||||
}
|
||||
|
||||
@@ -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 작업 등록기 클래스입니다.
|
||||
*
|
||||
* <p>이 클래스는 애플리케이션 컨텍스트가 리프레시될 때 실행되며,
|
||||
* {@link QuartzJob} 어노테이션이 붙은 모든 메소드를 찾아 Quartz 스케줄러에 등록합니다.</p>
|
||||
*
|
||||
* <p>주요 기능:</p>
|
||||
* <ul>
|
||||
* <li>애플리케이션 컨텍스트 내의 모든 빈을 검사</li>
|
||||
* <li>QuartzJob 어노테이션이 붙은 메소드 식별</li>
|
||||
* <li>AbstractBatchJob을 상속받은 클래스 식별</li>
|
||||
* <li>식별된 메소드를 Quartz 작업으로 등록</li>
|
||||
* <li>각 작업에 대한 JobDetail 및 Trigger 생성</li>
|
||||
* </ul>
|
||||
@@ -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<ContextRefreshedE
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* 애플리케이션 컨텍스트가 리프레시될 때 호출되는 메소드.
|
||||
* 모든 빈을 순회하며 QuartzJob 어노테이션이 붙은 메소드를 찾아 Quartz 작업으로 등록합니다.
|
||||
*
|
||||
* 애플리케이션 컨텍스트가 리프레시될 때 호출되는 메소드입니다.
|
||||
* QuartzJob 어노테이션이 붙은 메소드와 AbstractBatchJob을 상속받은 클래스를 등록합니다.
|
||||
*
|
||||
* @param event ContextRefreshedEvent 객체
|
||||
*/
|
||||
@Override
|
||||
public void onApplicationEvent(@NonNull ContextRefreshedEvent event) {
|
||||
registerQuartzJobs();
|
||||
registerAbstractJobs();
|
||||
}
|
||||
|
||||
/**
|
||||
* QuartzJob 어노테이션이 붙은 모든 메소드를 찾아 Quartz 스케줄러에 등록합니다.
|
||||
*/
|
||||
private void registerQuartzJobs() {
|
||||
for (String beanName : applicationContext.getBeanDefinitionNames()) {
|
||||
Object bean = applicationContext.getBean(beanName);
|
||||
Class<?> 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<ContextRefreshedE
|
||||
private Trigger createTrigger(QuartzJob quartzJobAnnotation, JobDetail jobDetail) {
|
||||
return TriggerBuilder.newTrigger()
|
||||
.forJob(jobDetail)
|
||||
.withIdentity(quartzJobAnnotation.name() + "Trigger", quartzJobAnnotation.group())
|
||||
.withIdentity(quartzJobAnnotation.jobName() + "Trigger", quartzJobAnnotation.group())
|
||||
.withSchedule(CronScheduleBuilder.cronSchedule(quartzJobAnnotation.cronExpression()))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* AbstractBatchJob을 상속받은 모든 클래스를 찾아 Quartz 스케줄러에 등록합니다.
|
||||
*/
|
||||
public void registerAbstractJobs() {
|
||||
Map<String, AbstractBatchJob> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
15
batch-quartz/src/main/resources/mapper/post/PostMapper.xml
Normal file
15
batch-quartz/src/main/resources/mapper/post/PostMapper.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.spring.domain.post.mapper.PostMapper">
|
||||
<insert id="save">
|
||||
insert
|
||||
into app_post (content, title)
|
||||
values (#{post.content}, #{post.title})
|
||||
</insert>
|
||||
|
||||
<select id="findAll" resultType="Post">
|
||||
select * from app_post;
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -3,8 +3,7 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.spring.domain.user.mapper.AppUserMaper">
|
||||
<select id="findByLoginId" resultType="com.spring.domain.user.entity.AppUser">
|
||||
select *
|
||||
from app_user
|
||||
<select id="findByLoginId" resultType="AppUser">
|
||||
select * from app_user
|
||||
</select>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user