2009/01/02 11:26
Spring Batch 1.0에서 2.0으로 진화하기- 1. ItemReader/ItemWriter(1)
2009/01/02 11:26 in Spring 지식 공유하기/Batch

Spring Batch는 SpringSource와 Accenture가 합심해서 함께 진행하고 있는 프로젝트로, Spring portfolio 중 가장 빠르게 보급되고 있고, 앞으로도 보급될 프로젝트로 주목 받고 있습니다.
현재는 2.0M3까지 나온 상태고 4월에 2.0 정식 버전이 릴리스 될 예정입니다.
2.0에서는 내부, 외부적으로 많은 부분이 개선될(이미 개선 된) 것 같습니다. 이미 적용된 부분이 많은데요, 자세한 내용은 change log(2.0m1, 2.0m2, 2.0m3)를 참고하시면 됩니다.
몇 번에 걸쳐서 2.0에서 개선된 기능이나 추가된 내용, 또는 내부 구현 방법이 어떻게 변경됐는지 살펴볼 생각입니다.
우선 배치 잡 구성에 있어서 핵심이 되는 ItemReader와 ItemWriter를 살펴보겠습니다.
1.x와 2.x의 인터페이스를 비교해보면 바로 차이점을 느낄 수가 있습니다.
# 1.x
- ItemReader
# 2.x
ItemReader와 ItemWriter 모두 3개이던 메소드가 하나로 줄었습니다. 각 인터페이스의 존재에 딱 맞는 메소드 하나씩만 남게 됐습니다. ItemReader에 있는 mark(), reset()과 ItemWriter에 있는 flush()와 clear() 모두 꼭 필요한 기능은 아니면서, 오히려 사용자에게 혼란을 주어서 2.x에서는 제거하기로 했다고 합니다.
이들 기능이 필요한 없는 ItemReader와 ItemWriter를 위해 1.x에서는 AbstractItemReader와 AbstractItemWriter라는 클래스를 두고, 사용하지 않는 이들 메소드의 빈 구현체를 작성해두고, read()와 write()만 구현해서 사용하는 방법을 쓰기도 했습니다.
2.x에서는 이 두 클래스가 아예 사라졌습니다.
ItemReader의 mark()와 reset()은 chunk item 처리시 버퍼 기능을 구현을 하는데 사용되던 API 였습니다. 2.x에서 두 메소드가 없어지면서 이 기능 자체도 함께 사라지게 됐습니다. 그래서 기존 1.x에 존재하던 AbstractBufferedItemReaderItemStream클래스는 AbstractItemCountingItemStreamItemReader로 리팩터링 됐습니다. 이름에서도 알 수 있듯이, 1.x의 AbstractBufferedItemReaderItemStream클래스가 ItemReader의 mark()와 reset()을 구현한 버퍼 기능을 제공하는 추상 클래스였지만, 2.x의 AbstractItemCountingItemStreamItemReader는 버퍼 기능이 아닌 아이템을 읽은 횟수에 따른 재시도 처리만을 제공해주고 있습니다. 1.x의 AbstractBufferedItemReaderItemStream클래스는 다음 그림에서 볼 수있듯이, 주요 ItemReader 구현체들이 모두 상속하는 중요한 추상 클래스 중 하나였습니다.
이 클래스에서 버퍼 기능을 제거함에 따라서 해당 클래스와 이를 상속받는 클래스들의 구현 자체도 더욱 명료해졌습니다.
# 1.x AbstractBufferedItemReaderItemStream.read()
# 2.x AbstractItemCountingItemStreamItemReader.read()
멤버변수의 수도 9개에서 4개로 줄었습니다. 불필요한 기능을 제거하고, API를 명확하게 했으며, 내부 구현도 단순화한 좋은 리팩터링 사례로 생각됩니다.
현재는 2.0M3까지 나온 상태고 4월에 2.0 정식 버전이 릴리스 될 예정입니다.
2.0에서는 내부, 외부적으로 많은 부분이 개선될(이미 개선 된) 것 같습니다. 이미 적용된 부분이 많은데요, 자세한 내용은 change log(2.0m1, 2.0m2, 2.0m3)를 참고하시면 됩니다.
몇 번에 걸쳐서 2.0에서 개선된 기능이나 추가된 내용, 또는 내부 구현 방법이 어떻게 변경됐는지 살펴볼 생각입니다.
우선 배치 잡 구성에 있어서 핵심이 되는 ItemReader와 ItemWriter를 살펴보겠습니다.
1.x와 2.x의 인터페이스를 비교해보면 바로 차이점을 느낄 수가 있습니다.
# 1.x
- ItemReader
public interface ItemReader {
Object read() throws Exception, UnexpectedInputException, NoWorkFoundException, ParseException;
void mark() throws MarkFailedException;
void reset() throws ResetFailedException;
}
public interface ItemWriter {
void write(Object item) throws Exception;
void flush() throws FlushFailedException;
void clear() throws ClearFailedException;
}
Object read() throws Exception, UnexpectedInputException, NoWorkFoundException, ParseException;
void mark() throws MarkFailedException;
void reset() throws ResetFailedException;
}
public interface ItemWriter {
void write(Object item) throws Exception;
void flush() throws FlushFailedException;
void clear() throws ClearFailedException;
}
# 2.x
public interface ItemReader<T> {
T read() throws Exception, UnexpectedInputException, ParseException;
}
public interface ItemWriter<T> {
void write(List<? extends T> items) throws Exception;
}
T read() throws Exception, UnexpectedInputException, ParseException;
}
public interface ItemWriter<T> {
void write(List<? extends T> items) throws Exception;
}
ItemReader와 ItemWriter 모두 3개이던 메소드가 하나로 줄었습니다. 각 인터페이스의 존재에 딱 맞는 메소드 하나씩만 남게 됐습니다. ItemReader에 있는 mark(), reset()과 ItemWriter에 있는 flush()와 clear() 모두 꼭 필요한 기능은 아니면서, 오히려 사용자에게 혼란을 주어서 2.x에서는 제거하기로 했다고 합니다.
이들 기능이 필요한 없는 ItemReader와 ItemWriter를 위해 1.x에서는 AbstractItemReader와 AbstractItemWriter라는 클래스를 두고, 사용하지 않는 이들 메소드의 빈 구현체를 작성해두고, read()와 write()만 구현해서 사용하는 방법을 쓰기도 했습니다.
2.x에서는 이 두 클래스가 아예 사라졌습니다.
ItemReader의 mark()와 reset()은 chunk item 처리시 버퍼 기능을 구현을 하는데 사용되던 API 였습니다. 2.x에서 두 메소드가 없어지면서 이 기능 자체도 함께 사라지게 됐습니다. 그래서 기존 1.x에 존재하던 AbstractBufferedItemReaderItemStream클래스는 AbstractItemCountingItemStreamItemReader로 리팩터링 됐습니다. 이름에서도 알 수 있듯이, 1.x의 AbstractBufferedItemReaderItemStream클래스가 ItemReader의 mark()와 reset()을 구현한 버퍼 기능을 제공하는 추상 클래스였지만, 2.x의 AbstractItemCountingItemStreamItemReader는 버퍼 기능이 아닌 아이템을 읽은 횟수에 따른 재시도 처리만을 제공해주고 있습니다. 1.x의 AbstractBufferedItemReaderItemStream클래스는 다음 그림에서 볼 수있듯이, 주요 ItemReader 구현체들이 모두 상속하는 중요한 추상 클래스 중 하나였습니다.
이 클래스에서 버퍼 기능을 제거함에 따라서 해당 클래스와 이를 상속받는 클래스들의 구현 자체도 더욱 명료해졌습니다.
# 1.x AbstractBufferedItemReaderItemStream.read()
public Object read() throws Exception, UnexpectedInputException, NoWorkFoundException, ParseException {
currentItemCount++;
if (shouldReadBuffer) {
if (itemBufferIterator.hasNext()) {
return itemBufferIterator.next();
}
else {
// buffer is exhausted, continue reading from file
shouldReadBuffer = false;
itemBufferIterator = null;
}
}
Object item = doRead();
itemBuffer.add(item);
return item;
}
currentItemCount++;
if (shouldReadBuffer) {
if (itemBufferIterator.hasNext()) {
return itemBufferIterator.next();
}
else {
// buffer is exhausted, continue reading from file
shouldReadBuffer = false;
itemBufferIterator = null;
}
}
Object item = doRead();
itemBuffer.add(item);
return item;
}
# 2.x AbstractItemCountingItemStreamItemReader.read()
public T read() throws Exception, UnexpectedInputException, ParseException {
currentItemCount++;
return doRead();
}
currentItemCount++;
return doRead();
}
멤버변수의 수도 9개에서 4개로 줄었습니다. 불필요한 기능을 제거하고, API를 명확하게 했으며, 내부 구현도 단순화한 좋은 리팩터링 사례로 생각됩니다.
Prev
Rss Feed


