Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
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
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

[프록시팩토리] 프록시 팩토리-예제2, CGLIB 본문

인프런/[인프런] 스프링 핵심 원리 - 고급

[프록시팩토리] 프록시 팩토리-예제2, CGLIB

Seung__ 2021. 12. 31. 00:04

1. 구체 클래스만 있는 경우 


@Test
@DisplayName("구체클래스만 있으면 CGLIB 사용")
void concreteProxy(){
    ConcreteService target = new ConcreteService();
    ProxyFactory proxyFactory = new ProxyFactory(target);
    proxyFactory.addAdvice(new TimeAdvice());
    ConcreteService proxy = (ConcreteService) proxyFactory.getProxy();
    log.info("targetClass={}", target.getClass());
    log.info("proxyClass={}", proxy.getClass());

    proxy.call();

    //프록시팩토리 쓸 때 만 사용 가능.
    assertThat(AopUtils.isAopProxy(proxy)).isTrue();
    assertThat(AopUtils.isJdkDynamicProxy(proxy)).isFalse();
    assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
}
INFO hello.proxy.proxyfactory.ProxyFactoryTest - targetClass=class hello.proxy.common.service.ConcreteService
INFO hello.proxy.proxyfactory.ProxyFactoryTest - proxyClass=class hello.proxy.common.service.ConcreteService$$EnhancerBySpringCGLIB$$e01fc80b
INFO hello.proxy.common.advice.TimeAdvice - TimeProxy 실행
INFO hello.proxy.common.service.ConcreteService - ConcreteService call 호출
INFO hello.proxy.common.advice.TimeAdvice - TimeProxy 종료 resultTime = 17
  • ...$$EnhancerBySpringCGLIB$$e01fc80b : 기대한 대로 CGLIB을 사용했다.

 

 

2. ProxyTargetClass 사용


  • 인터페이스가 있어도 CGLIB를 사용하고 싶은 경우
  • setProxyTargetClass(true)
@Test
@DisplayName("ProxyTargetClass 옵션을 사용하면 인터페이스가 있어도 CGLIB 사용, 클래스 기반 프록시 사용")
void proxyTargetClass(){
    ServiceInterface target = new ServiceImpl();
    ProxyFactory proxyFactory = new ProxyFactory(target);

    proxyFactory.setProxyTargetClass(true); //true : 항상 CGLIB

    proxyFactory.addAdvice(new TimeAdvice());
    ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
    log.info("targetClass={}", target.getClass());
    log.info("proxyClass={}", proxy.getClass());

    proxy.save();

    //프록시팩토리 쓸 때 만 사용 가능.
    assertThat(AopUtils.isAopProxy(proxy)).isTrue();
    assertThat(AopUtils.isJdkDynamicProxy(proxy)).isFalse();
    assertThat(AopUtils.isCglibProxy(proxy)).isTrue();
}
INFO hello.proxy.proxyfactory.ProxyFactoryTest - targetClass=class hello.proxy.common.service.ServiceImpl
INFO hello.proxy.proxyfactory.ProxyFactoryTest - proxyClass=class hello.proxy.common.service.ServiceImpl$$EnhancerBySpringCGLIB$$e730a17b
INFO hello.proxy.common.advice.TimeAdvice - TimeProxy 실행
INFO hello.proxy.common.service.ServiceImpl - save 호출
INFO hello.proxy.common.advice.TimeAdvice - TimeProxy 종료 resultTime = 16
  • ...ServiceImpl$$EnhancerBySpringCGLIB$$e730a17b : 인터페이스가 있지만 CGLIB를 사용했다.
  • 따라서 ServiceImpl 클래스를 상속받아 클래스 기반의 프록시를 만들었다.

 

 

3. 프록시 팩토리의 기술 선택 방법


  • 대상에 인터페이스가 있으면: JDK 동적 프록시, 인터페이스 기반 프록시
  • 대상에 인터페이스가 없으면: CGLIB, 구체 클래스 기반 프록시
  • proxyTargetClass=true : CGLIB, 구체 클래스 기반 프록시, 인터페이스 여부와 상관없음

 

 

 

4. 정리


  • 프록시 팩토리의 서비스 추상화 덕분에 구체적인 CGLIB, JDK 동적 프록시 기술에 의존하지 않고, 
  • 매우 편리하게 동적 프록시를 생성할 수 있다.
  • 프록시의 부가 기능 로직도 특정 기술에 종속적이지 않게 Advice 하나로 편리하게 사용할 수 있었다.
  • 이것은 프록시 팩토리가 내부에서 JDK 동적 프록시인 경우 InvocationHandler 가 Advice 를 호출하도록 개발해두고, CGLIB인 경우 MethodInterceptor 가 Advice 를 호출하도록 기능을 개발해두었기 때문이다.

 

* 참고

  • 스프링 부트는 AOP를 적용할 때 기본적으로 proxyTargetClass=true 로 설정해서 사용한다.
  • 따라서 인터페이스가 있어도 항상 CGLIB를 사용해서 구체 클래스를 기반으로 프록시를 생성한다.

 

5.  GitHub : 211231 Proxy Factory 2


 

GitHub - bsh6463/Spring_Advanced_2

Contribute to bsh6463/Spring_Advanced_2 development by creating an account on GitHub.

github.com

 

 

Comments