Spring Batch : ItemReaderAdapter

    이 글은 인프런 정수원님의 강의를 복습하며 작성한 글입니다

    ItemReaderAdatper

    스프링 배치에서는 여러가지 ItemReader를 전달해준다. 그런데 이 ItemReader를 통해서는 이미 존재하고 있는 DAO / 다른 서비스를 ItemReader에서 사용할 수는 없다. 그래서 Spring Batch는 중간에 Adapater 패턴을 도입해서 기존 서비스의 값을 ItemReader로 읽어올 수 있도록 도와준다. 

     

    ItemReaderAdapater는 자바의 Reflection 기술을 사용한다. ItemReaderAdapter에 불러올 객체와 메서드 명을 셋팅해주면, ItemReaderAdapater는 Invoker를 만들고, invoker를 invoke로 타겟 객체의 타겟 메서드를 불러오는 역할을 해준다. 

     

    ItemReaderAdapter 동작방식

    ItemReaderAdapater는 다른 ItemReader와 동일하게 동작한다. read 메서드가 invoke로 대체된다고 보면 되고, invoke를 할 때 마다 item / cnt가 계속 올라간다. 그리고 cnt가 Chunk Size가 될 때까지 읽어오는 것을 반복해서 Input Chunk를 만들어준다. 만들어진 Input Chunk는 Processor로 전달된다. 

     

    ItemReadAdapter 사용

    ItemReaderAdapater를 사용하기 위해서는 ItemReaderAdapater를 만들어준다. 그리고 아래 API를 이용해 Reflection을 할 수 있도록 해준다.

    • setTargetMethod : 대상이 되는 메서드를 설정해준다. 이 때, 메서드 이름을 String으로 전달한다.
    • setTargetObject : 대상이 되는 객체를 넣어준다. DI / 생성자 등으로 설정해줄 수 있다.
    • setArguments : 메서드에 넘겨줄 객체를 설정한다. Object 배열로 전달 가능하다. 

     

    ItemReaderAdapter 약간의 구조

    ItemReaderAdapater 선언
    Step

    ItemReaderAdapter는 다음과 같이 객체를 선언해서 만들고, 그 값을 ItemReader API에 전달해주는 형식으로 사용한다. 

    ItemReaderAdapater.read()

    itemReaderAdapater는 itemReader처럼 쓰이고, 이 reader는 결국 read() 메서드를 구현해두었다. 이 read 메서드에서는 invokeDelgateMethod()라는 메서드를 호출한다. 

    AbstractingMethodInvokingDelegator.invokeDelegateMethodWithArguments

    invokdeDelgateMethod를 하면 위의 클래스 + 메서드로 넘어온다. 여기서 invoker를 만들고, 전달받은 매개변수를 넣은 후, invoking을 해준다. 

    target 객체 / target 메서드

    invoke를 하게 되면 다음과 같이 내가 설정했던 타겟 객체의 타겟 메서드로 넘어오게 된다. 여기서 Item을 반환해주면, 이 값이 input chunk의 Item으로 계속 쌓이게 된다. 

     

    테스트 코드

    https://github.com/chickenchickenlove/springbatchstudy/tree/main/SpringBatchLecture/main/java/io/springbatch/springbatchlecture/dbitemreader

     

    테스트 코드 실행

    테스트 코드를 실행하면, 끊임없이 반복되는 것을 볼 수 있다. 왜냐하면 Chunk Processor는 기본적으로 데이터 소스 전체를 처리하는데, 내가 짠 코드는 read를 할 때 마다 값을 전달해주기 때문에 끝이 없다. 실제로 내가 가지고 있던 Service와 연결하려면 이 부분을 해결해봐야 할 것 같다. 

     

    @Configuration
    @RequiredArgsConstructor
    public class CustomItemReaderAdapterConfig {
    
        private final StepBuilderFactory stepBuilderFactory;
        private final JobBuilderFactory jobBuilderFactory;
    
        private final static int CHUNK_SIZE = 100;
    
        @Bean
        public Job JdbcJob() {
            return jobBuilderFactory
                    .get("ItemReaderAdapterJob")
                    .start(step100())
                    .incrementer(new RunIdIncrementer())
                    .build();
        }
    
        @Bean
        public Step step100() {
            return stepBuilderFactory
                    .get("ItemReaderAdapterStep")
                    .<Customer, Customer>chunk(CHUNK_SIZE)
                    .reader(customerItemReader())
                    .processor(new org.springframework.batch.item.ItemProcessor<Customer, Customer>() {
                        @Override
                        public Customer process(Customer item) throws Exception {
                            return Customer.builder()
                                    .id(item.getId())
                                    .lastName(item.getLastName().toUpperCase(Locale.ROOT))
                                    .firstName(item.getFirstName().toUpperCase(Locale.ROOT))
                                    .birthDate(item.getBirthDate())
                                    .build();
                        }
                    })
                    .writer(new org.springframework.batch.item.ItemWriter<Customer>() {
                        @Override
                        public void write(List<? extends Customer> items) throws Exception {
                            System.out.println("=========================");
                            System.out.println("ItemReaderAdapter WORK");
                            items.forEach(customer -> System.out.println("customer = " + customer));
                            System.out.println("=========================");
                        }
                    })
                    .build();
        }
    
        @Bean
        public ItemReaderAdapter<Customer> customerItemReader() {
    
            ItemReaderAdapter readerAdapter = new ItemReaderAdapter<>();
            readerAdapter.setTargetObject(new CustomService());
            readerAdapter.setTargetMethod("joinMember");
    
    
            return readerAdapter;
    
        }
        
    }

     

    댓글

    Designed by JB FACTORY