배치는 실시간으로 처리하는것이 아닌 처리할 작업물들을 모아 위그림처럼 한번에 처리하는 것이다
예를들어 1년간 접속하지않은 모든 회원들을 휴먼계정으로 처리하는 등 일반적인 비즈니스로직이 아닌 상황에 대해서 처리해주는 프로세스이다
@Import(BatchConfigurationSelector.class)
boolean modular() default false;
해당 어노테이션 BatchConfigurationSelector를 임포트한다
modular라는 속성이 있는데 이는 ApplicationContext마다 다르게 배치를 사용할때의 옵션이다
true값을 주게되면 @Bean이아닌 ApplicationContextFactory를 직접만들어 해당 Context에 주입한뒤 사용해야한다
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
if (attributes.containsKey("modular") && attributes.getBoolean("modular")) {
imports = new String[] { ModularBatchConfiguration.class.getName() };
}
else {
imports = new String[] { SimpleBatchConfiguration.class.getName() };
}
}
위에서 설명했던 modular에따라 객체가 다르게 생성되는걸 볼 수 있다
하지만 둘은 결국 부모가 같다
@Configuration(proxyBeanMethods = false)
@Import(ScopeConfiguration.class)
public abstract class AbstractBatchConfiguration implements ImportAware, InitializingBean {
@Bean
public JobBuilderFactory jobBuilders() throws Exception {
return this.jobBuilderFactory;
}
@Bean
public StepBuilderFactory stepBuilders() throws Exception {
return this.stepBuilderFactory;
}
@Bean
public abstract JobRepository jobRepository() throws Exception;
@Bean
public abstract JobLauncher jobLauncher() throws Exception;
@Bean
public abstract JobExplorer jobExplorer() throws Exception;
@Bean
public JobRegistry jobRegistry() throws Exception {
return this.jobRegistry;
}
@Bean
public abstract PlatformTransactionManager transactionManager() throws Exception;
@Override
public void afterPropertiesSet() throws Exception {
this.jobBuilderFactory = new JobBuilderFactory(jobRepository());
this.stepBuilderFactory = new StepBuilderFactory(jobRepository(), transactionManager());
}
}
다음과같이 배치를 사용하기위해 Factory부터 Repository, Launcher등의 객체를 생성해주며 Bean이 초기화 될 당시 Fatcory에 직접 객체를 생성하는것 또한볼 수 있다
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration=
BatchAutoConfiguration은 spring-autocofigure-metadata.properties파일에보면 자동으로 등록되어있다
@Import({ BatchConfigurerConfiguration.class, DatabaseInitializationDependencyConfigurer.class })
public class BatchAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.batch.job", name = "enabled", havingValue = "true", matchIfMissing = true)
public JobLauncherApplicationRunner jobLauncherApplicationRunner(JobLauncher jobLauncher, JobExplorer jobExplorer,
JobRepository jobRepository, BatchProperties properties) {
JobLauncherApplicationRunner runner = new JobLauncherApplicationRunner(jobLauncher, jobExplorer, jobRepository);
String jobNames = properties.getJob().getNames();
if (StringUtils.hasText(jobNames)) {
runner.setJobNames(jobNames);
}
return runner;
}
@Conditional(OnBatchDatasourceInitializationCondition.class)
static class DataSourceInitializerConfiguration {
...
}
static class OnBatchDatasourceInitializationCondition extends OnDatabaseInitializationCondition {
OnBatchDatasourceInitializationCondition() {
super("Batch", "spring.batch.jdbc.initialize-schema", "spring.batch.initialize-schema");
}
}
}
JobLauncherApplicationRunner라는 ApplicationRunner가 등록되는데 이를 통해 Job들이 실행된다
이 외에 Datasource관련된 클래스들은 정보 DB에 DML, DDL과 관련된 객체들이다
public void run(String... args) throws JobExecutionException {
launchJobFromProperties(StringUtils.splitArrayElementsIntoProperties(args, "="));
}
protected void launchJobFromProperties(Properties properties) throws JobExecutionException {
JobParameters jobParameters = this.converter.getJobParameters(properties);
executeLocalJobs(jobParameters);
executeRegisteredJobs(jobParameters);
}
private void executeLocalJobs(JobParameters jobParameters) throws JobExecutionException {
for (Job job : this.jobs) {
...
execute(job, jobParameters);
}
}
private void executeRegisteredJobs(JobParameters jobParameters) throws JobExecutionException {
...
for (String jobName : jobsToRun) {
...
Job job = this.jobRegistry.getJob(jobName);
...
execute(job, jobParameters);
}
}
run이 호출되면 해당 args배열을 파싱해 JobParameter를 찾아온다음 local에있는 Job과 Repository에 저장되어있는 Job에 전달해 실행시킨다
protected void execute(Job job, JobParameters jobParameters) {
JobParameters parameters = getNextJobParameters(job, jobParameters);
JobExecution execution = this.jobLauncher.run(job, parameters);
if (this.publisher != null) {
this.publisher.publishEvent(new JobExecutionEvent(execution));
}
}
다음과같이 JobParameter, JobExecution객체를 만들어 JobLauncher를 통해 실행한 뒤 Event를 발행한다