AOP(3) - Dynamic Proxy

๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ๋ž€?

  • ๋Ÿฐํƒ€์ž„ ์‹œ์— ๋™์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ์˜ค๋ธŒ์ ํŠธ

  • java์˜ reflection์„ ์ด์šฉํ•ด์„œ proxy ๊ฐ์ฒด ์ƒ์„ฑ(java.lang.reflect)

  • ํƒ€๊ฒŸ ์ธํ„ฐํŽ˜์ด์Šค์™€ ๋™์ผํ•œ ํ˜•ํƒœ๋กœ ์ƒ์„ฑ

    • ํ”„๋ก์‹œ ๋Œ€์ƒ์˜ ๊ฐ์ฒด๊ฐ€ ์ตœ์†Œ ํ•˜๋‚˜ ์ด์ƒ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค๋ฉด JDK ๋™์  ํ”„๋ก์‹œ๋ฅผ ์ด์šฉํ•˜๋ฉด๋œ๋‹ค.

  • FactoryBean(ํŒฉํ† ๋ฆฌ๋นˆ)์„ ํ†ตํ•ด ์ƒ์„ฑ

reflection์ด๋ž€ ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ํด๋ž˜์Šค์˜ ์ •๋ณด๋ฅผ ๋ถ„์„ํ•ด ๋‚ด๋Š” ํ”„๋กœ๊ทธ๋žจ ๊ธฐ๋ฒ•์„ ๋งํ•œ๋‹ค.

์Šคํ”„๋ง์˜ ๋นˆ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ํด๋ž˜์Šค ์ด๋ฆ„๊ณผ Property๋กœ ์ •์˜ํ•œ๋‹ค.

์Šคํ”„๋ง์€ ์ง€์ •๋œ ํด๋ž˜์Šค ์ด๋ฆ„์„ ๊ฐ€์ง€๊ณ  reflection์„ ์ด์šฉํ•ด ํ•ด๋‹น ํด๋ž˜์Šค์˜ ๊ฐ์ฒด(object)๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

https://www.baeldung.com/wp-content/uploads/2017/10/springaop-process.png

์Šคํ”„๋ง์—์„œ๋Š” JDK ๋™์  ํ”„๋ก์‹œ์™€ CGLib ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค.

JDK Dynamic Proxy

JDK Dynamic Proxy๋Š” Proxy Factory์— ์˜ํ•ด ๋Ÿฐํƒ€์ž„์‹œ ๋™์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ์˜ค๋ธŒ์ ํŠธ์ด๋‹ค. JDK Dynamic Proxy๋Š” ๋ฐ˜๋“œ์‹œ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ •์˜๋˜์–ด์žˆ๊ณ , ์ธํ„ฐํŽ˜์ด์Šค์— ๋Œ€ํ•œ ๋ช…์„ธ๋ฅผ ๊ธฐ์ค€์œผ๋กœ Proxy๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์ฆ‰, ์ธํ„ฐํŽ˜์ด์Šค ์„ ์–ธ์— ๋Œ€ํ•œ ๊ฐ•์ œ์„ฑ์ด ์žˆ๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

๋‚ด๋ถ€์ ์œผ๋กœ JDK Dynamic Proxy์—์„œ๋Š” InvationHandler๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด ๋งŒ๋“ค์–ด์ง€๋Š”๋ฐ, invoke ํ•จ์ˆ˜๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•˜์—ฌ Proxy์˜ ์œ„์ž„ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์ด ๊ณผ์ •์—์„œ ๊ฐ์ฒด์— ๋Œ€ํ•œ Reflection ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด ๊ตฌํ˜„ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํผํฌ๋จผ์Šค ํ•˜๋ฝ์˜ ์›์ธ์ด ๋˜๊ธฐ๋„ ํ•œ๋‹ค.

๊ตฌํ˜„๊ณผ์ •

  1. Proxy.newProxyInstance() ๋ฅผ ํ†ตํ•œ ํ”„๋ก์‹œ ์ƒ์„ฑ

  2. Proxy.newProxyInstance() ํ˜ธ์ถœํ•  ๋•Œ ์ „๋‹ฌํ•˜๋Š” InvocationHandler ์ธํ„ฐํŽ˜์ด์Šค์˜ ๋‹จ์ผ ๋ฉ”์†Œ๋“œ์ธ invoke()์— ๋ถ€๊ฐ€๊ธฐ๋Šฅ์„ ๋‹จ ํ•œ๋ฒˆ๋งŒ ๊ตฌํ˜„ํ•จ์œผ๋กœ์จ ์ฝ”๋“œ ์ค‘๋ณต์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์ด๋‚˜๋ฏน ํ”„๋ก์‹œ ๊ฐ์ฒด๋Š” ํด๋ž˜์Šค ํŒŒ์ผ ์ž์ฒด๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋นˆ ๊ฐ์ฒด๋กœ ๋“ฑ๋ก์ด ๋ถˆ๊ฐ€ํ•˜๋‹ค.

public interface ServiceTest {
  void print();
}

@Service
public class ServiceTestImpl implements ServiceTest {
  @Async
  public void print() {
  }
}

@RestController
public class HelloController {
  private final ServiceTest service;
  public HelloController(ServiceTest service) {
    this.service = service;
    System.out.println(service.getClass());
  }
}
class com.sun.proxy.$Proxy60

์œ„์˜ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ์ธํ„ฐํŽ˜์ด์Šค(ServiceTest)๋ฅผ ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด(ServiceTestImpl)์ด ์žˆ๋‹ค. print๋ฌธ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด JDK ๋™์  ํ”„๋ก์‹œ๊ฐ€ ๋“ค์–ด๊ฐ„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šคํ”„๋ง AOP๋ฅผ ์ด์šฉํ•˜์—ฌ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด๋ณผ ๊ฒƒ์ด๋‹ค. ์Šคํ”„๋ง ๋‚ด๋ถ€์—์„œ ์ œ๊ณตํ•˜๋Š” FactoryBean ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•  ๊ฒƒ์ด๋‹ค.

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setInterfaces(ServiceTest.class);
proxyFactory.setTarget(new ServiceTestImpl());
proxyFactory.addAdvice(new ServiceAdvice());
final ServiceTest proxy = (ServiceTest) proxyFactory.getProxy();
System.out.println(proxy.getClass());
class com.sun.proxy.$Proxy60

์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์žˆ์–ด์„œ JDK ๋™์  ํ”„๋ก์‹œ๊ฐ€ ์ƒ์„ฑ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

CGLib Proxy

CGLIB Proxy๋Š” ์ˆœ์ˆ˜ Java JDK ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ CGLIB๋ผ๋Š” ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. CGLIB์˜ Enhancer ํด๋ž˜์Šค๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ Proxy๋ฅผ ์ƒ์„ฑํ•˜๋ฉฐ, ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์—†์–ด๋„ Proxy๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. CGBLIB Proxy๋Š” ํƒ€๊ฒŸ ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์•„ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— Proxy๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ค์–ด์•ผํ•˜๋Š” ์ˆ˜๊ณ ๋ฅผ ๋œ ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ, ์ƒ์†์„ ์ด์šฉํ•˜๋ฏ€๋กœ final์ด๋‚˜ private์™€ ๊ฐ™์ด ์ƒ์†์— ๋Œ€ํ•ด ์˜ค๋ฒ„๋ผ์ด๋”ฉ์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ์—๋Š” Aspect๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ๋‹ค.

CGLIB Proxy๋Š” ๋ฐ”์ดํŠธ ์ฝ”๋“œ๋ฅผ ์กฐ์ž‘ํ•ด์„œ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ JDK Dynamic Proxy๋ณด๋‹ค ํผํฌ๋จผ์Šค๊ฐ€ ๋น ๋ฅธ ์žฅ์ ์ด ์žˆ๋‹ค.

@Service
public class ServiceTestImpl {
  @Async
  public void print() {
  }
}

@RestController
public class HelloController {
  private final ServiceTest service;
  public HelloController(ServiceTest service) {
    this.service = service;
    System.out.println(service.getClass());
  }
}
class me.wonwoo.ServiceTestImpl$$EnhancerBySpringCGLIB$$bababcc1

์ธํ„ฐํŽ˜์ด์Šค ์—†์ด ์ƒ์„ฑํ•œ ๊ฒฝ์šฐ์—๋Š” CGLib ํ”„๋ก์‹œ๋กœ ์ƒ์„ฑ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์Šคํ”„๋ง AOP๋ฅผ ์ด์šฉํ•˜์—ฌ ํ”„๋ก์‹œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด๋ณผ ๊ฒƒ์ด๋‹ค. ์Šคํ”„๋ง ๋‚ด๋ถ€์—์„œ ์ œ๊ณตํ•˜๋Š” FactoryBean ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•  ๊ฒƒ์ด๋‹ค.

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new ServiceTestImpl());
proxyFactory.addAdvice(new ServiceAdvice());
final ServiceTest proxy = (ServiceTest) proxyFactory.getProxy();
System.out.println(proxy.getClass());
class me.wonwoo.ServiceTestImpl$$EnhancerBySpringCGLIB$$bababcc1

์ธํ„ฐํŽ˜์ด์Šค ์—†์ด ์ƒ์„ฑํ•œ ๊ฒฝ์šฐ์—๋Š” CGLib ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

ํ”„๋ก์‹œ ๊ฐ์ฒด์— CGLib์„ ๊ฐ•์ œํ™” ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ•์ œํ™”๋ฅผ ํ•˜๊ฒŒ๋˜๋ฉด ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ CGLib ํ”„๋ก์‹œ ๊ฐ์ฒด๊ฐ€ ์‚ฝ์ž…๋œ๋‹ค.

@EnableAsync(proxyTargetClass = true)
@EnableCaching(proxyTargetClass = true)

JDK Dynamic Proxy vs CGLIB

๋‘ ๋ฐฉ์‹์˜ ์ฐจ์ด๋Š” ์ธํ„ฐํŽ˜์ด์Šค์˜ ์œ ๋ฌด ๋กœ์„œ, AOP์˜ ํƒ€๊ฒŸ์ด ๋˜๋Š” ํด๋ž˜์Šค๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์˜€๋‹ค๋ฉด JDK Dynamic Proxy ์‚ฌ์šฉ, ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด CGLIB ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•œ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋–ป๊ฒŒ ์„ค์ •ํ•˜๋А๋ƒ์— ๋”ฐ๋ผ์„œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ–ˆ๋‹ค ํ•˜๋”๋ผ๋„ CGLIB ๋ฐฉ์‹์„ ๊ฐ•์ œํ•˜๊ฑฐ๋‚˜ AspectJ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • CGLIB(์Šคํ”„๋ง์˜ XML ์„ค์ • ํŒŒ์ผ์— ๋นˆ์„ ๋“ฑ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•)

<!--proxy-targetclass="true"์„ ์ถ”๊ฐ€ํ•˜์—ฌ CGLIB์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•œ๋‹ค.-->
<aop:config proxy-target-class="true"> 
    <!-- other beans defined here... -->
    <aop:pointcut id="fooServiceMethods" expression="execution(* x.y.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceMethods"/>
</aop:config>
  • AspectJ : ์–ด๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜์˜ ์Šคํƒ€์ผ ์„ ํ˜ธ

<aop:aspectj-autoproxy proxy-target-class="true">

์ฐธ์กฐ ํŽ˜์ด์ง€

Last updated

Was this helpful?