티스토리 뷰
1. Scope 그리고 스프링의 기본 Scope, Singleton
앞선 장에서 설명드리지 않았으나 무척이나 중요한 역할을 하던 컴포넌트가 있습니다.
바로 배치가 실행될 때 Spring Bean을 생성하는 시점을 명시하는 @JobScope와 @StepScope 입니다.
@Bean
@StepScope
public ListItemReader<Member> unPaidMemberReader() {
log.info("********** This is unPaidMemberReader");
List<Member> activeMembers = memberRepository.findByStatusEquals(MemberStatus.ACTIVE);
log.info(" - activeMember SIZE : " + activeMembers.size());
List<Member> unPaidMembers = new ArrayList<>();
for (Member member : activeMembers) {
if(member.isUnpaid()) {
unPaidMembers.add(member);
}
}
log.info(" - unPaidMember SIZE : " + unPaidMembers.size());
return new ListItemReader<>(unPaidMembers);
}
먼저 두 컴포넌트를 이해하기 위해 SpringFramework에서 기본 Scope에 대해 알아봅시다.
Spring은 기본 Scope는 Singleton입니다. 클래스 Instance를 오직 1개만 만들어 참조하는 것이 Singleton입니다.
ApplicationContext context =
new GenericXmlApplicationContext("applicationContext.xml");
BondDao bondDao1 = context.getBean("bondDAO",BondDao.class);
BondDao bondDao2 = context.getBean("bondDAO",BondDao.class);
System.out.println("bondDao 1 : " + bondDao1);
System.out.println("bondDao 2 : " + bondDao2);
System.out.println("bondDao1 == bondDao2 ? " + bondDao1==bondDao2);
위의 코드는 다음의 결과를 보여줍니다.
bondDao1 : spring.dao.BondDAO@2903874
bondDao2 : spring.dao.BondDAO@2903874
bondDao1 == bondDao2 ? true
같은 Bean에서 각각 가져와서 만든 bondDao1 그리고 bondDao2 인스턴스는 서로 같은 값을 참조하고 있으니 동일한 인스턴스입니다. 즉 ApplicationContext로부터 Bean을 가져올 때 마다 인스턴스를 새로 생성하지 않는다는 의미입니다.
단순하게 응용을 해 보겠습니다.
위 애플리케이션의 bondDao 클래스에서 데이터 조회를 하는 select메소드를 호출한다고 합니다. 이 애플리케이션은 여러명이 사용할테니 동시다발적으로 bondDao에 있는 select메소드를 호출할 수 있겠죠. 만약 1000개의 호출요청을 들어올 때 그에 맞춰 1000개의 인스턴스를 생성한다면 그야말로 엄청난 자원낭비가 될 수 있습니다. 어차피 동일한 Bean에서 가져올테니까요.
그래서 SpringFramework은 각각에 Beans들을 기본적으로 Singletone으로 관리를 하고 여러 Thread에서 이를 공유하여 사용할 수 있게 해줍니다. 다시말해 스프링의 기본 Scope는 Singletone 입니다.
2. @JobScope 그리고 @StepScope
@JobScope 그리고 @StepScope는 방금 소개한 스프링의 기본 Scope인 Singleton과는 대치되는 역할입니다. @JobScope 그리고 @StepScope가 붙어 있는 메소드에서는 바로 그 메소드의 실행지점에 해당 @Bean을 Spring Bean으로 생성합니다.
이것은 Bean의 생성시점이 스프링 애플리케이션이 실행되는 시점이 아닌 @JobScope/@StepScope가 명시된 메소드의 실행될 때까지 지연시킨 다는 것(LateBinding)을 의미합니다.
SpringBatch에서는 이렇게 Bean 생성을 지연시킴으로써 얻게되는 장점은 다음과 같습니다.
1. JobParameter를 특정메서드가 실행하는 시점까지 지연시켜 할당시킬 수 있습니다.애플리케이션이 구동되는 시점이 아니라 비즈니스로직이 구현되는 어디든 JobParameter를 할당함으로 유연한 설계를 가능하게 합니다.
2. 병렬처리에 안전합니다.앞서 unPaidMemberReader메소드는 Step 구성요소(Itemreader=읽고, ItemProcessor=처리하고, ItemWriter=쓰고)중 데이터를 읽어오는 역할이 정의된 메소드입니다.이 메소드가 서로 다른 Step으로 부터 동시에 병렬실행이 된다면 서로의 상태를 간섭을 받게 될 수 있습니다. 하지만 앞서 @StepScope를 명시해 놓을으로써 각각의 Step에서 실행될 때 서로의 상태를 침범하지 않고 처리를 완료할 수 있습니다.
@JobScope는 Step선언문에서만 사용이 가능합니다.
@StepScope는 Step을 구성하는 ItemReader, ItemWriter, ItemProcessor에서 사용 가능합니다.
public class unPaidMemberConfig {
private MemberRepository memberRepository;
private JobBuilderFactory jobBuilderFactory;
private StepBuilderFactory stepBuilderFactory;
@Bean
public Job unPaidMemberJob(
) {
log.info("********** This is unPaidMemberJob");
return jobBuilderFactory.get("unPaidMemberJob")
.preventRestart()
.start(this.unPaidMemberJobStep(null)) //1
.build();
}
@Bean
@JobScope //2
public Step unPaidMemberJobStep(@Value("#{jobParameters[requestDate]}") String requestDate) {
log.info("********** This is unPaidMemberJobStep");
log.info("********** This is requestDate of unPaidMemberJobStep : {}", requestDate); //3
return stepBuilderFactory.get("unPaidMemberJobStep")
.<Member, Member> chunk(10)
.reader(unPaidMemberReader())
.processor(this.unPaidMemberProcessor())
.writer(this.unPaidMemberWriter())
.build();
}
...
}
1 | unPiadMemberJobStep의 파라미터로 null을 주었습니다. |
2 | Bean으로 등록된 메소드에 @JobScope를 선언하였습니다. 이제 이 Bean은 실행되는 시점에 스프링의 Bean으로 생성됩니다 JobParameter는 다음과 같이 SpEL로 선언해서 사용합니다. @Value("#{jobParameters[파라미터명]}") |
3 | 해당 메소드에 실행되면서 실제 JobParameter로 할당받는 값을 log를 직접 찍어 확인하고자 합니다. |
그리고 다음과 같이 JobParameter 값을 변경한 이후 애플리케이션을 실행시켜 봅시다.
Run/Debug Configurations를 열어 Program arguments의 값을 requestDate=20190903_1 로 입력 후 OK버튼을 누릅니다.
실행완료이후 consolelog에서 방금전 작성한 로그의 값을 확인해 보면 requestDate의 값이 201903_1로 할당받은 것을 확인하실 수 있습니다.
********** This is requestDate of unPaidMemberJobStep : 20190903_1
이렇게 JobParameters는 Step 또는 Step의 구성요소라 할 수 있는 읽고=ItemReader, 처리하고=ItemProcessor, 쓰고=ItemWriter와 같은 SpringBatch 컴포넌트 Bean의 생성 시점에 호출할 수 있습니다.
3. @JobScope(또는 StepScope) 와 JabParameter
지금까지의 내용을 살펴보면 @JobScope(StepScope)가 선언된 @Bean은 오직 그 Bean이 실행되는 시점에 스프링의 Bean으로 생성합니다.
또 그렇게 @JobScope(또는 StepScope)를 명시하면서 까지 스프링 Bean 생성을 지연시킬만 이유는 결국 새로운 JobInstance를 생성할 수 있는 JobParameter의 값을 애플리케이션 실행레벨(구동시점)이 아닌 비즈니스구현부 단계에서 할당되게 함으로써 보다 훨씬 더 유연한 프로세스 설계를 가능하게 하기 때문입니다. 이쯤이면 @JobScope(또는 StepScope)는 결국 JobParameter의 바인딩(LateBinding)을 위해, 더 나아가선 JobInstance의 생성과 서로 밀접하고 연쇄적인 관계를 이해하실 수 있을 것입니다.
'Server' 카테고리의 다른 글
Actuator 를 사용하여 서버의 상태를 체크하기 (0) | 2021.10.26 |
---|---|
[Java] ObjectMapper 를 사용한 Map to JSONObject (0) | 2021.10.26 |
Batch 프로그램과 스케줄러 (0) | 2021.10.21 |
Spring Batch 관련 링크 모음 (0) | 2021.10.21 |
Vi / Vim 에서 줄 번호 보여주기 명령 (0) | 2021.09.01 |
[Java] Files와 Path를 사용한 파일 읽기 & 쓰기 개발하기 (0) | 2021.08.30 |