스프링 레시피 CH2.9 후처리기를 만들어 POJO 검증/수정하기

2023. 12. 16. 18:44spring

과제

모든 빈 인스턴스, 또는 특정 타입의 인스턴스를 생성할 때 해당 빈 프로퍼티를 어떤 기준에 따라 검증/수정하라

해결책

빈 후처리기를 이용하면 초기화 *콜백 메서드 전후에 원하는 로직을 빈에 적용할 수 있다.

(*@Bean의 initMethod 속성 or @PostConstruct를 붙인 메서드)

 

빈 후처리기의 가장 중요한 특징은 IoC 컨테이너 내부의 모든 빈 인스턴스를 대상으로 한다.

보통 빈 후처리기는 빈 프로퍼티가 올바른지 체크하거나 어떤 기준에 따라 빈 프로퍼티를 변경 또는 전체 빈 인스턴스를 상대로 어떤 작업을 수행하는 용도로 쓰인다.

 

@Required는 스프링에 내장된 후처리기RequiredAnnotationBeanPostProcessor가 지원하는 애너테이션이다. RequiredAnnotationBeanPostProcessor 후처리기는 @Required를 붙인 모든 빈 프로퍼티가 설정되었는지 확인한다.

풀이

빈 후처리기를 이용하면 기존 POJO 코드를 전혀 건드릴 필요가 없다.

 

모든 빈 인스턴스를 처리하는 후처리기 생성하기

빈 후처리기는 BeanPostProcessor 인터페이스를 구현한 객체다. 이 인터페이스를 구현한 빈을 발견하면 스프링은 자신이 관장하는 모든 빈 인스턴스에 postProcessBeforeInitinalization(), postProcessAfterInitialization() 메서드를 적용한다.

public class AuditCheckBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("In AuditCheckBeanPostProcessor.postProcessBeforeInitialization," +
                "processing bean type: " + bean.getClass());
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

 

postProcessBeforeInitinalization(), postProcessAfterInitialization() 메서드는 하는 일이 없어도 반드시 원본 빈 인스턴스를 반환해야 한다.

 

클래스 레벨에 @Component를 붙이면 애플리케이션 컨텍스트에 빈 후처리기로 등록된다. 애플리케이션 컨텍스트는 BeanPostProcessor 구현 빈을 감지해 컨테이너 안에 있는 다른 빈 인스턴스에 일괄 적용한다.

 

주어진 빈 인스턴스만 처리하는 후처리기 생성하기

IoC 컨테이너는 자신이 생성한 빈 인스턴스를 모두 하나씩 빈 후처리기에 넘긴다. 만일 특정 타입의 빈만 후처리기를 적용하려면 인스턴스 타입을 체크하는 필터를 이용해 원하는 빈에만 후처리 로적을 적용할 수 있다.

 

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (bean instanceof Product product) {
        String productName = product.getName();
        System.out.println("In AuditCheckBeanPostProcessor.postProcessBeforeInitialization," +
                "processing Product: " + productName);
    }

    return bean;
}

=>
In AuditCheckBeanPostProcessor.postProcessBeforeInitialization,processing Product: AAA
In AuditCheckBeanPostProcessor.postProcessBeforeInitialization,processing Product: CD-RW
In AuditCheckBeanPostProcessor.postProcessBeforeInitialization,processing Product: DVD-RW
Shopping cart 1 contains [Product{name='AAA', price=2.5}, Product{name='CD-RW', price=1.5}]
Shopping cart 2 contains [Product{name='DVD-RW', price=3.0}]