2009/01/02 11:26

Spring Batch 1.0에서 2.0으로 진화하기- 1. ItemReader/ItemWriter(1)

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
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;
}

# 2.x
public interface ItemReader<T> {
   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;
}

# 2.x AbstractItemCountingItemStreamItemReader.read()
public T read() throws Exception, UnexpectedInputException, ParseException {
   currentItemCount++;
   return doRead();
}

멤버변수의 수도 9개에서 4개로 줄었습니다. 불필요한 기능을 제거하고, API를 명확하게 했으며, 내부 구현도 단순화한 좋은 리팩터링 사례로 생각됩니다.
Trackback 0 Comment 0