Spring/AOP

CGLIB

an_jjin 2024. 11. 11. 18:58
๐Ÿ’ก ๊น€์˜ํ•œ๋‹˜์˜ ์Šคํ”„๋ง ํ•ต์‹ฌ ์›๋ฆฌ ๊ณ ๊ธ‰ํŽธ ๊ฐ•์˜๋ฅผ ๋“ฃ๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

CGLIB

CGLIB: Code Generator Library

CGLIB๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ๋ฅผ ์กฐ์ž‘ํ•ด์„œ ๋™์ ์œผ๋กœ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ์ˆ ์„ ์ œ๊ณตํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

CGLIB๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์—†์–ด๋„ ๊ตฌ์ฒด ํด๋ž˜์Šค๋งŒ ๊ฐ€์ง€๊ณ  ๋™์  ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ค์–ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

CGLIB๋Š” ์›๋ž˜๋Š” ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ๋ฐ, ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์Šคํ”„๋ง ๋‚ด๋ถ€ ์†Œ์Šค ์ฝ”๋“œ์— ํฌํ•จํ–ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์Šคํ”„๋ง์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋ณ„๋„์˜ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•„๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ฐธ๊ณ ๋กœ ์šฐ๋ฆฌ๊ฐ€ CGLIB๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๊ฑฐ์˜ ์—†๋‹ค. ์ดํ›„์— ์„ค๋ช…ํ•  ์Šคํ”„๋ง์˜ ProxyFactory๋ผ๋Š” ๊ฒƒ์ด ์ด ๊ธฐ์ˆ ์„ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ฒŒ ๋„์™€์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ๋„ˆ๋ฌด ๊นŠ์ด์žˆ๊ฒŒ ํŒŒ๊ธฐ ๋ณด๋‹ค๋Š” CGLIB๊ฐ€ ๋ฌด์—‡์ธ์ง€ ๋Œ€๋žต ๊ฐœ๋…๋งŒ ์žก์œผ๋ฉด ๋œ๋‹ค. ์˜ˆ์ œ ์ฝ”๋“œ๋กœ CGLIB๋ฅผ ๊ฐ„๋‹จํžˆ ์ดํ•ดํ•ด๋ณด์ž.

CGLIB ์ฝ”๋“œ

JDK ๋™์  ํ”„๋ก์‹œ์—์„œ ์‹คํ–‰ ๋กœ์ง์„ ์œ„ํ•ด InvocationHandler๋ฅผ ์ œ๊ณตํ–ˆ๋“ฏ์ด, CGLIB๋Š” MethodInterceptor๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

 public interface MethodInterceptor extends Callback {
     Object intercept(Object obj, Method method, Object[] args, MethodProxy
 proxy) throws Throwable;
 }
  • obj : CGLIB๊ฐ€ ์ ์šฉ๋œ ๊ฐ์ฒด
  • method : ํ˜ธ์ถœ๋œ ๋ฉ”์„œ๋“œ
  • args : ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ ์ „๋‹ฌ๋œ ์ธ์ˆ˜
  • proxy : ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์— ์‚ฌ์šฉ

TimeMethodInterceptor

@Slf4j
public class TimeMethodInterceptor implements MethodInterceptor {

    private final Object target;

    public TimeMethodInterceptor(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        log.info("TimeProxy ์‹คํ–‰");
        long startTime = System.currentTimeMillis();

        Object result = methodProxy.invoke(target, args);

        long endTime = System.currentTimeMillis();
        long resultTime = endTime - startTime;
        log.info("TimeProxy ์ข…๋ฃŒ resultTime={}", resultTime);
        return result;
    }
}
  • TimeMethodInterceptor๋Š” MethodInterceptor ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ด์„œ CGLIB ํ”„๋ก์‹œ์˜ ์‹คํ–‰ ๋กœ์ง์„ ์ •์˜ํ•œ๋‹ค.
  • JDK ๋™์  ํ”„๋ก์‹œ๋ฅผ ์„ค๋ช…ํ•  ๋•Œ ์˜ˆ์ œ์™€ ๊ฑฐ์˜ ๊ฐ™์€ ์ฝ”๋“œ์ด๋‹ค
  • Object target: ํ”„๋ก์‹œ๊ฐ€ ํ˜ธ์ถœํ•  ์‹ค์ œ ๋Œ€์ƒ
  • proxy.invoke(target, args): ์‹ค์ œ ๋Œ€์ƒ์„ ๋™์ ์œผ๋กœ ํ˜ธ์ถœํ•œ๋‹ค.
  • ์ฐธ๊ณ ๋กœ method๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๋˜์ง€๋งŒ, CGLIB๋Š” ์„ฑ๋Šฅ์ƒ MethodProxy proxy๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.

์‹คํ–‰ ์˜ˆ์‹œ ์ฝ”๋“œ

 @Slf4j
 public class CglibTest {
     @Test
     void cglib() {
         ConcreteService target = new ConcreteService();
         Enhancer enhancer = new Enhancer();
         enhancer.setSuperclass(ConcreteService.class);
         enhancer.setCallback(new TimeMethodInterceptor(target));
         ConcreteService proxy = (ConcreteService)enhancer.create();
         log.info("targetClass={}", target.getClass());
         log.info("proxyClass={}", proxy.getClass());
         proxy.call();
     }
}

ConcreteService๋Š” ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์—†๋Š” ๊ตฌ์ฒด ํด๋ž˜์Šค์ด๋‹ค. ์—ฌ๊ธฐ์— CGLIB๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•ด๋ณด์ž.

  • Enhancer
    • CGLIB๋Š” Enhancer๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  • enhancer.setSuperclass(ConcreteService.class)
    • CGLIB๋Š” ๊ตฌ์ฒด ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›์•„์„œ ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์–ด๋–ค ๊ตฌ์ฒด ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›์„์ง€ ์ง€์ •ํ•œ๋‹ค.
  • enhancer.setCallback(new TimeMethodInterceptor(target))
    • ํ”„๋ก์‹œ์— ์ ์šฉํ•  ์‹คํ–‰ ๋กœ์ง์„ ํ• ๋‹นํ•œ๋‹ค.
  • enhancer.create()
    • ํ”„๋ก์‹œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
    • ์•ž์„œ ์„ค์ •ํ•œ enhancer.setSuperclass(ConcreteService.class)์—์„œ ์ง€์ •ํ•œ ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›์•„์„œ ํ”„๋ก์‹œ๊ฐ€ ๋งŒ๋“ค์–ด์ง„๋‹ค. 

JDK ๋™์  ํ”„๋ก์‹œ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„(implement)ํ•ด์„œ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ ๋‹ค.

CGLIB๋Š” ๊ตฌ์ฒด ํด๋ž˜์Šค๋ฅผ ์ƒ์†(extends) ํ•ด์„œ ํ”„๋ก์‹œ๋ฅผ ๋งŒ๋“ ๋‹ค.