-
[Spring] 스프링 핵심 원리 - 빈 후처리기Study/Spring 2025. 4. 21. 23:54
빈 후처리기 (BeanPostProcessor)
- @Bean이나 컴포넌트 스캔에 의해 스프링이 빈 저장소에 등록할 목적으로 생성한 객체를 빈 저장소에 등록하기 직전에 조작하고 싶다면 BeanPostProcessor를 사용하면 됨
- 빈 객체를 프록시로 교체하는 것도 가능함
BeanPostProcessor에 의한 스프링 빈 등록 과정
- 생성 : 스프링 빈 대상이 되는 객체를 생성한다. (@Bean, 컴포넌트 스캔 모두 포함)
- 전달 : 생성된 객체를 빈 저장소에 등록하기 직전에 BeanPostProcessor에 전달한다.
- 후 처리 작업 : BeanPostProcessor는 전달받은 스프링 빈 객체를 조작하거나 다른 객체로 바꿔치기 할 수 있다.
- 등록 : BeanPostProcessor는 빈을 반환한다. 전달된 빈을 그대로 반환하면 해당 빈이 등록되고, 바꿔치기 하면 다른 객체가 빈 저장소에 등록된다.
참고) BeanPostProcessor 인터페이스 - 스프링 제공
더보기package org.springframework.beans.factory.config; public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
- 빈 후처리기를 사용하려면 해당 인터페이스를 구현하고, 스프링 빈으로 등록하면 됨
- postProcessBeforeInitialization : 객체 생성 이후 @PostConstruct 같은 초기화 발행하기 전에 호출되는 포스트 프로세서
- postProcessAfterInitialization : 객체 생성 이후 @PostConstruct 같은 초기화 발행한 다음에 호출되는 포스트 프로세서
스프링이 제공하는 빈 후처리기
- 스프링은 프록시를 생성하기 위한 빈 후처리기를 이미 만들어서 제공함
build.gradle 추가
implementation 'org.springframework.boot:spring-boot-starter-aop'
- 해당 라이브러리를 추가하면 aspectjweaver라는 aspectJ 관련 라이브러리를 등록하고, 스프링부트가 AOP 관련 클래스를 자동으로 스프링 빈에 등록함
- 스프링부트가 활성화하는 빈은 AopAutoConfiguration 클래스 참고
- 스프링부트가 없던 시절에는 @EnableAspectJAutoProxy를 직접 사용해야 했는데, 이 부분을 스프링부트가 자동으로 처리해줌
자동 프록시 생성기 (AutoProxyCreator)
- 위 스프링부트 자동 설정으로 AnnotationAwareAspectJAutoProxyProcessor 라는 빈 후처리기가 스프링 빈에 자동 등록됨
- 이름 그대로 자동으로 프록시를 생성해주는 빈 후처리기
- 스프링 빈으로 등록된 Advisor들을 자동으로 찾아서 프록시가 필요한 곳에 프록시를 적용해줌
- Advisor 안에는 Pointcut과 Advice가 이미 포함되어 있음
- 따라서 Advisor만 알고 있으면 Pointcut으로 어떤 스프링 빈에 프록시를 적용해야 할지 알 수 있고, Advice로 부가 기능을 적용하면 됨
작동 과정
- 생성 : 스프링이 스프링 빈 대상이 되는 객체를 생성한다. (@Bean, 컴포넌트 스캔 모두 포함)
- 전달 : 생성된 객체를 빈 저장소에 등록하기 직전에 빈 후처리기에 전달한다.
- 모든 Advisor 빈 조회 : 스프링 컨테이너에서 모든 Advisor를 조회한다.
- 프록시 적용 대상 체크 : 앞서 조회한 Advisor에 포함되어 있는 Pointcut를 사용해서 해당 객체가 프록시 적용 대상인지 판단한다.
- 대상 객체의 클래스 정보와 모든 메서드를 Pointcut에 모두 매칭하고 조건이 하나라도 만족하면 프록시 적용 대상이 됨
- 프록시 생성 : 프록시 적용 대상이면 프록시를 생성하고 반환해서 프록시를 스프링 빈으로 등록함
- 프록시 적용 대상이 아니라면 원본 객체를 반환해서 원본 객체를 스프링 빈으로 등록함
- 빈 등록 : 반환된 객체는 스프링 빈으로 등록됨
생성된 프록시
- 프록시는 내부에 Advisor와 실제 호출해야 할 대상 객체(target)을 알고 있음
예시)
더보기Advisor Bean 등록
import org.springframework.aop.Advisor; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.NameMatchMethodPointcut; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AutoProxyConfig { @Bean public Advisor advisor1() { NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); pointcut.setMappedNames("request*", "order*", "save*"); TimeAdvice advice = new TimeAdvice(); // advisor = pointcut + advice return new DefaultPointcutAdvisor(pointcut, advice); } }
- BeanPostProcessor는 등록하지 않고 Advisor만 Bean으로 등록함
- 스프링은 자동 프록시 생성기(AnnotationAwareAspectJAutoProxyCreator)라는 빈 후처리기를 자동으로 등록해줌
중요: Pointcut의 2가지 용도
- 프록시 적용 여부 판단 - 빈 생성 단계
- 자동 프록시 생성기는 Pointcut을 사용해 해당 빈이 프록시를 생성할 필요가 있는지/없는지 체크함
- 클래스 + 메서드 조건을 모두 비교함
- 모든 메서드를 체크하기 위해 Pointcut 조건에 하나하나 매칭해봄
- 조건에 부합하는 것이 하나라도 있으면 프록시를 생성함
- 만약 조건에 부합하는 것이 하나도 없으면 프록시를 생성할 필요가 없으므로 프록시를 생성하지 않음
- Advice 적용 여부 판단 - 사용 단계
- 프록시가 호출되었을 때 부가 기능인 Advice를 적용할지 말지 Pointcut을 보고 판단함
- ex) 대상 빈 : 프록시 적용된 orderController, pointcut : request*로 시작하는 메서드
- orderController의 request() : 현재 포인트컷 조건에 만족하므로 프록시는 Advice를 먼저 호출하고, 이후 target을 호출함
- orderController의 noLog() : 현재 포인트컷 조건에 만족하지 않으므로 Advice를 호출하지 않고 바로 target만 호출함
- ex) 대상 빈 : 프록시 적용된 orderController, pointcut : request*로 시작하는 메서드
- 프록시가 호출되었을 때 부가 기능인 Advice를 적용할지 말지 Pointcut을 보고 판단함
- 참고)
- 프록시를 모든 곳에 생성하는 것은 비용 낭비이므로, 꼭 필요한 곳에 최소한의 프록시를 적용해야 함
- 자동 프록시 생성기는 모든 스프링 빈에 프록시 빈을 적용하지 않고 Pointcut으로 한번 필터링해서 Advisor가 사용될 가능성이 있는 곳에만 프록시를 생성함
하나의 프록시, 여러 Advisor 적용
Q. 스프링 빈으로 등록된 advisor1, advisor2가 제공하는 포인트컷의 조건을 모두 만족하면 스프링의 프록시 자동 생성기는 프록시를 몇 개 생성할까?
- 프록시 자동 생성기는 프록시를 '하나만' 생성함
- 프록시 팩토리가 생성하는 프록시는 내부에 여러 advisor들을 포함할 수 있기 때문에 프록시를 여러 개 생성해서 비용을 낭비할 이유가 없음
프록시 자동 생성기 상황별 정리
- advisor1의 포인트컷만 만족 : 프록시 1개 생성, 프록시에 advisor1만 포함
- advisor1, advisor2의 포인트컷을 모두 만족 : 프록시 1개 생성, 프록시에 advisor1과 advisor2 모두 포함
- advisor1, advisor2의 포인트컷을 모두 만족하지 않음 : 프록시가 생성되지 않음
참고
'Study > Spring' 카테고리의 다른 글
[Spring] 스프링 핵심 원리 - 스프링 AOP (0) 2025.04.23 [Spring] 스프링 핵심 원리 - @Aspect AOP (0) 2025.04.22 [Spring] 스프링 핵심 원리 - 스프링이 지원하는 프록시 (0) 2025.04.20 [Spring] 스프링 핵심 원리 - 동적 프록시 (0) 2025.04.20 [Spring] 스프링 핵심 원리 - ThreadLocal (1) 2025.04.19 - @Bean이나 컴포넌트 스캔에 의해 스프링이 빈 저장소에 등록할 목적으로 생성한 객체를 빈 저장소에 등록하기 직전에 조작하고 싶다면 BeanPostProcessor를 사용하면 됨