[Spring] 동적 ν”„λ‘μ‹œ 기술 (3) - CGLIB

2022. 6. 2. 12:59ㆍBackend/🌿 Spring

μ§€λ‚œ ν¬μŠ€νŒ…μ—μ„œ JDK 동적 ν”„λ‘μ‹œλ₯Ό μ μš©ν•˜κΈ° μœ„ν•΄μ„  μΈν„°νŽ˜μ΄μŠ€κ°€ ν•„μˆ˜λΌκ³  ν–ˆλ‹€. μΈν„°νŽ˜μ΄μŠ€κ°€ μ—†κ³  ꡬ체 클래슀만 μžˆλŠ” 경우, CGLIB λ°”μ΄νŠΈμ½”λ“œ μ‘°μž‘ 라이브러리λ₯Ό 톡해 동적 ν”„λ‘μ‹œλ₯Ό μ μš©ν•  수 μžˆλ‹€. 

 

CGLIB : Code Generator Library

  • CGLIB : λ°”μ΄νŠΈ μ½”λ“œλ₯Ό μ‘°μž‘ν•΄μ„œ λ™μ μœΌλ‘œ 클래슀λ₯Ό μƒμ„±ν•˜λŠ” κΈ°μˆ μ„ μ œκ³΅ν•˜λŠ” 라이브러리 
  • μΈν„°νŽ˜μ΄μŠ€ 없이 ꡬ체클래슀 만으둜 동적 ν”„λ‘μ‹œλ₯Ό λ§Œλ“€μ–΄ λ‚Ό 수 μžˆλ‹€.
  • μ›λž˜λŠ” μ™ΈλΆ€ 라이브러리인데, μŠ€ν”„λ§ ν”„λ ˆμž„μ›Œν¬ λ‚΄λΆ€ μ†ŒμŠ€ μ½”λ“œμ— 포함돼 μžˆλ‹€. 
  • 참고둜 κ°œλ°œμžκ°€ 직접 CGLIB λ₯Ό μ‚¬μš©ν•˜λŠ” κ²½μš°λŠ” 거의 μ—†λ‹€. λ‹€μŒ ν¬μŠ€νŒ…μ—μ„œ λ‹€λ£° ProxyFactory κ°€ CGLIB μ‚¬μš©μ„ νŽΈλ¦¬ν•˜κ²Œ 도와주기 λ•Œλ¬Έμ—, λ„ˆλ¬΄ 깊이있게 νŒŒκΈ°λ³΄λ‹€λŠ” CGLIB κ°€ 무엇인지 λŒ€λž΅ κ°œλ…λ§Œ 작으면 λœλ‹€. 

 

 

CGLIB 예제 μ½”λ“œ

동적 ν”„λ‘μ‹œμ—μ„œλŠ” InvcationHandler λ₯Ό μ œκ³΅ν–ˆλ“―, CGLIB μ—μ„œλŠ” MethodInterceptor λ₯Ό μ œκ³΅ν•œλ‹€.  MethodInterceptor μΈν„°νŽ˜μ΄μŠ€λŠ” Callback 클래슀(μ›λž˜λŠ” μΈν„°νŽ˜μ΄μŠ€)λ₯Ό 상속받고 μžˆλ‹€λŠ” μ •λ„λ§Œ κΈ°μ–΅ν•΄λ‘μž. 

package org.springframework.cglib.proxy;

import java.lang.reflect.Method;

public interface MethodInterceptor extends Callback {
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}


// ConcreteService.java
@Slf4j
public class ConcreteService { // μΈν„°νŽ˜μ΄μŠ€ μ—†λ‹€.
    public void call() {
        log.info("ConcreteService 호좜");
    }
}

 

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 ν”„λ‘μ‹œμ˜ μ‹€ν–‰ λ‘œμ§μ„ μ •μ˜ν•œλ‹€.
  • Object target : ν”„λ‘μ‹œκ°€ ν˜ΈμΆœν•  μ‹€μ œ λŒ€μƒ
  • methodProxy.invoke(target, args) : μ‹€μ œ λŒ€μƒμ„ λ™μ μœΌλ‘œ ν˜ΈμΆœν•œλ‹€. JDK 동적 ν”„λ‘μ‹œλŠ” Method νƒ€μž…μ„ ν˜ΈμΆœν–ˆμ§€λ§Œ, CGLIB λŠ” MethodProxy νƒ€μž… 호좜을 μ„±λŠ₯상 ꢌμž₯ν•œλ‹€.

 

CglibTest

@Slf4j
public class CglibTest {

    @Test
    void cglib() {

        // μΈν„°νŽ˜μ΄μŠ€κ°€ μ—†λŠ” 클래슀
        ConcreteService target = new ConcreteService();

        // CGLIB λ₯Ό λ§Œλ“œλŠ” μ½”λ“œ
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ConcreteService.class);
        // ꡬ체 클래슀λ₯Ό 기반으둜 ConcreteService λ₯Ό 상속받은 ν”„λ‘μ‹œλ₯Ό λ§Œλ“€μ–΄λ‚΄μ•Όν•œλ‹€.

        // MethodInterceptor κ°€ CallBack 을 μƒμ†λ°›μŒμ„ μœ λ…
        enhancer.setCallback(new TimeMethodInterceptor(target));
        ConcreteService proxy = (ConcreteService) enhancer.create();
        log.info("targetClass={}", target.getClass());
        log.info("proxyClass={}", proxy.getClass());

        proxy.call();

    }
}

μ‹€ν–‰ κ²°κ³Ό

  • Enhancer : CGLIB λŠ” Enhancer λ₯Ό μ‚¬μš©ν•΄ ν”„λ‘μ‹œλ₯Ό μƒμ„±ν•œλ‹€. 
  • ν”„λ‘μ‹œλ₯Ό μ μš©ν•  ꡬ체 클래슀λ₯Ό 상속받아 ν”„λ‘μ‹œλ₯Ό μƒμ„±ν•œλ‹€. setSuperClass 의 인자둜 μ–΄λ–€ 클래슀λ₯Ό 상속받을지 μ§€μ •ν•œλ‹€.
  • enhancer.setCallback(new TimeMethodInterceptor(target)) : ν”„λ‘μ‹œμ— μ μš©ν•  μ‹€ν–‰ λ‘œμ§μ„ ν• λ‹Ήν•œλ‹€. 
  • enhancer.create() : ꡬ체클래슀λ₯Ό 상속받은 ν›„ ν”„λ‘μ‹œλ₯Ό μƒμ„±ν•œλ‹€. 

 

CGLIB μ œμ•½

  • 클래슀 기반 ν”„λ‘μ‹œλŠ” μΈν„°νŽ˜μ΄μŠ€ 기반 ν”„λ‘μ‹œμ™€ 달리, 상속을 μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— λͺ‡κ°€μ§€ μ œμ•½μ΄ μ‘΄μž¬ν•œλ‹€.
  • λΆ€λͺ¨ 클래슀의 μƒμ„±μžλ₯Ό 체크해야 ν•œλ‹€.
  • μƒμ„±μžκ°€ ν•„μš”ν•˜λ‹€.
  • ν΄λž˜μŠ€μ— final ν‚€μ›Œλ“œκ°€ λΆ™μœΌλ©΄ 상속이 λΆˆκ°€λŠ₯ν•˜λ‹€. CGLIBμ—μ„œλŠ” μ˜ˆμ™Έκ°€ λ°œμƒν•œλ‹€.
  • λ©”μ„œλ“œμ— final ν‚€μ›Œλ“œκ°€ λΆ™μœΌλ©΄ ν•΄λ‹Ή λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”© ν•  수 μ—†λ‹€. CGLIBμ—μ„œλŠ” ν”„λ‘μ‹œ 둜직이 λ™μž‘ν•˜μ§€ μ•ŠλŠ”λ‹€.

 

정리

  • μΈν„°νŽ˜μ΄μŠ€κ°€ μžˆλŠ” κ²½μš°μ™€, 그렇지 μ•Šμ€ 경우λ₯Ό λͺ¨λ‘ μ•„μš°λ₯Ό 수 μžˆλŠ” 방법은 μ—†μ„κΉŒ?
  • 두 κΈ°μˆ μ„ ν•¨κ»˜ μ‚¬μš©ν•  땐 JDK 동적 ν”„λ‘μ‹œκ°€ μ œκ³΅ν•˜λŠ” InvocationHandler 와 CGLIB κ°€ μ œκ³΅ν•˜λŠ” MethodInterceptor λ₯Ό 각각 μ€‘λ³΅μœΌλ‘œ λ§Œλ“€μ–΄ κ΄€λ¦¬ν•΄μ•Όν•˜λ‚˜?
  • 이런 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ‹€μŒ ν¬μŠ€νŒ…μ—μ„  ProxyFactory λ₯Ό μ•Œμ•„λ³΄κ² λ‹€. 

 

 

μ½”λ“œ 및 자료 좜처

 

μŠ€ν”„λ§ 핡심 원리 - κ³ κΈ‰νŽΈ - μΈν”„λŸ° | κ°•μ˜

μŠ€ν”„λ§μ˜ 핡심 원리와 κ³ κΈ‰ κΈ°μˆ λ“€μ„ 깊이있게 ν•™μŠ΅ν•˜κ³ , μŠ€ν”„λ§μ„ μžμ‹ μžˆκ²Œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€., - κ°•μ˜ μ†Œκ°œ | μΈν”„λŸ°...

www.inflearn.com