Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 자바
- 백준
- SpringBoot
- Greedy
- 스프링 핵심 원리
- Android
- 김영한
- QueryDSL
- Exception
- JPQL
- Proxy
- 알고리즘
- 스프링 핵심 기능
- AOP
- jpa
- kotlin
- java
- db
- Thymeleaf
- JDBC
- 그리디
- 인프런
- springdatajpa
- Spring Boot
- transaction
- http
- 스프링
- spring
- pointcut
- Servlet
Archives
- Today
- Total
개발자되기 프로젝트
[빈 후처리기] Bean Post Processor 적용 본문
- 빈 후처리기를 사용해서 실제 객체 대신 프록시를 스프링 빈으로 등록해보자.
- 이렇게 하면 수동으로 등록하는 빈은 물론이고,
- 컴포넌트 스캔을 사용하는 빈까지 모두 프록시를 적용할 수 있다.
- 더 나아가서 설정 파일에 있는 수 많은 프록시 생성 코드도 한번에 제거할 수 있다.
1. PackageLogTracePostProcessor
@Slf4j
public class PackageLogTracePostProcessor implements BeanPostProcessor {
//특정 패키지 하위의 빈이 대상
private final String basePackage;
private final Advisor advisor;
public PackageLogTracePostProcessor(String basePackage, Advisor advisor) {
this.basePackage = basePackage;
this.advisor = advisor;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
log.info("beanName={}, bean={}", beanName, bean.getClass());
//프록시 적용대상 여부
//프록시 적용 대상이 아니면 원본 그대로 진행
String packageName = bean.getClass().getPackageName();
if (!packageName.startsWith(basePackage)){
return bean;
}
//프록시 대상이면 프록시를 만들어서 반환
ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.addAdvisor(advisor);
Object proxy = proxyFactory.getProxy();
log.info("create proxy: target={}, proxy={}", bean.getClass(), proxy.getClass());
return proxy;
}
}
- PackageLogTraceProxyPostProcessor 는 원본 객체를 프록시 객체로 변환하는 역할을 한다.
- 이때 프록시 팩토리를 사용하는데, 프록시 팩토리는 advisor 가 필요하기 때문에
이 부분은 외부에서 주입 받도록 했다. - 모든 스프링 빈들에 프록시를 적용할 필요는 없다.
- 여기서는 특정 패키지와 그 하위에 위치한 스프링 빈들만 프록시를 적용한다.
- 여기서는 hello.proxy.app 과 관련된 부분에만 적용하면 된다.
- 다른 패키지의 객체들은 원본 객체를 그대로 반환한다.
- 프록시 적용 대상의 반환 값을 보면 원본 객체 대신에 프록시 객체를 반환한다.
- 따라서 스프링 컨테이너에 원본 객체 대신에 프록시 객체가 스프링 빈으로 등록된다.
- 원본 객체는 스프링 빈으로 등록되지 않는다.
2. Configuration
@Slf4j
@Configuration
@Import({AppV1Config.class, AppV2Config.class})
public class BeanPostProcessorConfig {
@Bean
public PackageLogTracePostProcessor logTracePostProcessor(LogTrace logTrace){
return new PackageLogTracePostProcessor("hello.proxy.app", getAdvisor(logTrace));
}
private Advisor getAdvisor(LogTrace logTrace) {
//point cut
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("request*", "order*", "save*");
//advice
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
}
- @Import({AppV1Config.class, AppV2Config.class})
- V3는 컴포넌트 스캔으로 자동으로 스프링 빈으로 등록되지만,
- V1, V2 애플리케이션은 수동으로 스프링 빈으로 등록해야 동작한다.
- ProxyApplication 에서 등록해도 되지만 편의상 여기에 등록하자.
- @Bean logTraceProxyPostProcessor()
- 특정 패키지를 기준으로 프록시를 생성하는 빈 후처리기를 스프링 빈으로 등록한다.
- 빈 후처리기는 스프링 빈으로만 등록하면 자동으로 동작한다.
- 여기에 프록시를 적용할 패키지 정보( hello.proxy.app )와 어드바이저( getAdvisor(logTrace) )를 넘겨준다.
- 이제 프록시를 생성하는 코드가 설정 파일에는 필요 없다.
- 순수한 빈 등록만 고민하면 된다.
- 프록시를 생성하고 프록시를 스프링 빈으로 등록하는 것은 빈 후처리기가 모두 처리해준다.
3. 실행, 결과
@Import(BeanPostProcessorConfig.class)
@SpringBootApplication(scanBasePackages = "hello.proxy.app") //주의
public class ProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ProxyApplication.class, args);
}
@Bean
public LogTrace logTrace(){
return new ThreadLocalLogTrace();
}
}
- 로그(축약)
#v1 애플리케이션 프록시 생성 - JDK 동적 프록시
create proxy: target=v1.OrderRepositoryV1Impl proxy=class com.sun.proxy.$Proxy50
create proxy: target=v1.OrderServiceV1Impl proxy=class com.sun.proxy.$Proxy51
create proxy: target=v1.OrderControllerV1Impl proxy=class com.sun.proxy.$Proxy52
#v2 애플리케이션 프록시 생성 - CGLIB
create proxy: target=v2.OrderRepositoryV2 proxy=v2.OrderRepositoryV2$$EnhancerBySpringCGLIB$$x4
create proxy: target=v2.OrderServiceV2 proxy=v2.OrderServiceV2$$EnhancerBySpringCGLIB$$x5
create proxy: target=v2.OrderControllerV2 proxy=v2.OrderControllerV2$$EnhancerBySpringCGLIB$$x6
#v3 애플리케이션 프록시 생성 - CGLIB
create proxy: target=v3.OrderRepositoryV3 proxy=3.OrderRepositoryV3$$EnhancerBySpringCGLIB$$x1
create proxy: target=v3.orderServiceV3 proxy=3.OrderServiceV3$$EnhancerBySpringCGLIB$$x2
create proxy: target=v3.orderControllerV3 proxy=3.orderControllerV3$$EnhancerBySpringCGLIB$$x3
- 여기서는 생략했지만, 실행해보면 스프링 부트가 기본으로 등록하는 수 많은 빈들이 빈 후처리기를
- 통과하는 것을 확인할 수 있다.
- 여기에 모두 프록시를 적용하는 것은 올바르지 않다.
- 꼭 필요한 곳에만 프록시를 적용해야 한다.
- 여기서는 basePackage 를 사용해서 v1~v3 애플리케이션 관련 빈들만 프록시 적용 대상이 되도록 했다.
- v1: 인터페이스가 있으므로 JDK 동적 프록시가 적용된다.
- v2: 구체 클래스만 있으므로 CGLIB 프록시가 적용된다.
- v3: 구체 클래스만 있으므로 CGLIB 프록시가 적용된다.
- 컴포넌트 스캔에도 적용
- 여기서 중요한 포인트는 v1, v2와 같이 수동으로 등록한 빈 뿐만 아니라
- 컴포넌트 스캔을 통해 등록한 v3빈들도 프록시를 적용할 수 있다는 점이다.
- 이것은 모두 빈 후처리기 덕분이다
- 프록시 적용 대상 여부 체크
- 애플리케이션을 실행해서 로그를 확인해보면 알겠지만,
- 우리가 직접 등록한 스프링 빈들 뿐만 아니라 스프링 부트가 기본으로 등록하는 수 많은 빈들이
- 빈 후처리기에 넘어온다.
- 그래서 어떤 빈을 프록시로 만들 것인지 기준이 필요하다.
- 여기서는 간단히 basePackage 를 사용해서 특정 패키지를 기준으로
- 해당 패키지와 그 하위 패키지의 빈들을 프록시로 만든다.
- 스프링 부트가 기본으로 제공하는 빈 중에는 프록시 객체를 만들 수 없는 빈들도 있다.
- 따라서 모든 객체를 프록시로 만들 경우 오류가 발생한다.
4. GitHub : 220102 Bean Post Processor 3
'인프런 > [인프런] 스프링 핵심 원리 - 고급' 카테고리의 다른 글
[빈 후처리기] 스프링이 제공하는 빈 후처리기1 (0) | 2022.01.03 |
---|---|
[빈 후처리기] 정리 (0) | 2022.01.03 |
[빈 후처리기] 예제코드 2 - 객체 바꿔치기 (0) | 2022.01.02 |
[빈 후처리기] 예제코드 1 - 일반적인 스프링 빈 등록 (0) | 2022.01.02 |
[빈 후처리기] Bean Post Processor - 소개 (0) | 2022.01.02 |
Comments