TIL/Spring & Spring Batch

μŠ€ν”„λ§ ν”„λ‘μ‹œ νŒ©ν† λ¦¬

JoJobum 2023. 5. 24. 21:17
πŸ’‘ ν”„λ‘μ‹œ(Proxy) λž€?
ν΄λΌμ΄μ–ΈνŠΈκ°€ μ‹€μ œ μ‚¬μš©ν•˜λ €λŠ” λŒ€μƒμΈμ–‘ μš”μ²­μ„ λ°›μ•„ μ²˜λ¦¬ν•˜λŠ” μ—­ν• .
ν”„λ‘μ‹œμ—κ²Œ μš”μ²­μ„ λ„˜κ²¨λ°›μ•„ μ΅œμ’… μ²˜λ¦¬ν•˜λŠ” μ˜€λΈŒμ νŠΈλŠ” 타깃(Target).
타깃과 ν”„λ‘μ‹œμΈμ§€ ν΄λΌμ΄μ–ΈνŠΈκ°€ ꡬ별할 수 μ—†μ–΄μ•Ό ν•˜κΈ°μ— λ‘˜μ€ 같은 μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™•μž₯ν•΄μ•Ό 함.

 

ν”„λ‘μ‹œλŠ” μ‚¬μš© λͺ©μ μ— 따라 2κ°€μ§€λ‘œ λ‚˜λ‰¨

  • 뢀가적인 κΈ°λŠ₯ λΆ€μ—¬ ⇒ λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄
  • μ ‘κ·Ό μ œμ–΄ ⇒ ν”„λ‘μ‹œ νŒ¨ν„΄ 

 

ν”„λ‘μ‹œ νŒ¨ν„΄

객체 생성은 λΉ„μš© ⇒ μ΅œμ†Œν•œ, ν•„μš” μ‹œμ κΉŒμ§€ λ―Έλ£¨λŠ”κ²Œ μ’‹μŒ

  • 타깃에 λŒ€ν•œ μ ‘κ·ΌκΆŒν•œ μ œμ–΄ κ°€λŠ₯
  • 캐싱

 

ν”„λ‘μ‹œμ˜ 단점

  • ν”„λ‘μ‹œκ°€ λ©€λ²„λ³€μˆ˜λ‘œ 타깃 였브젝트 가지기에 타깃 μ˜€λΈŒμ νŠΈμ— 쒅속적
  • λ˜‘κ°™μ€ κΈ°λŠ₯ μˆ˜ν–‰ν•˜λŠ” ν”„λ‘μ‹œλΌλ„ μ—¬λŸ¬ 타깃에 μ μš©ν•˜λ €λ©΄ νƒ€κΉƒμ˜ 갯수 만큼 ν”„λ‘μ‹œ 생성해야 ν•˜κΈ°μ— μ½”λ“œ 쀑볡 λ°œμƒ
  • ν”„λ‘μ‹œλ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” λ©”μ†Œλ“œμ—λ„ νƒ€κΉƒμœΌλ‘œ μœ„μž„ν•˜λŠ” λ©”μ†Œλ“œ μž‘μ„±ν•΄μ•Ό 함

⇒ λ¦¬ν”Œλ ‰μ…˜ κΈ°λŠ₯을 μ‚¬μš©ν•˜μ—¬ ν”„λ‘μ‹œλ₯Ό λ™μ μœΌλ‘œ μƒμ„±ν•΄μ„œ ν•΄κ²°ν•΄λ³΄μž!

 

닀이내믹 ν”„λ‘μ‹œ

μΈν„°νŽ˜μ΄μŠ€μ˜ 유무둜 크게 λΆ„λ₯˜λ¨

  • μΈν„°νŽ˜μ΄μŠ€κ°€ μ‘΄μž¬ν•˜λŠ” 경우 ⇒ JDK 동적 ν”„λ‘μ‹œ
  • μΈν„°νŽ˜μ΄μŠ€κ°€ μ—†λŠ” 경우 ⇒ CGLIB

 

JDK 동적 ν”„λ‘μ‹œ

κ΅¬ν˜„ ν΄λž˜μŠ€κ°€ μ•„λ‹Œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό 기반으둜 ν”„λ‘μ‹œ 생성

reflect의 invocationHandlerλ₯Ό κ΅¬ν˜„ν•˜λ©΄ λ§Œλ“€ 수 있음

Object proxy = Proxy.newProxyInstance(ClassLoader       // ν΄λž˜μŠ€λ‘œλ”
                                    , Class<?>[]        // νƒ€κΉƒμ˜ μΈν„°νŽ˜μ΄μŠ€
                                    , InvocationHandler // νƒ€κΉƒμ˜ 정보가 ν¬ν•¨λœ Handler
              										  );

Proxy 객체 μƒμ„±λ˜λŠ” κ³Όμ •

  1. ProxyFactoryκ°€ νƒ€κΉƒμ˜ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μƒμ†ν•œ Proxy 객체 생성
  2. proxy 객체에 invocation Handlerλ₯Ό ν¬ν•¨μ‹œμΌœ ν•˜λ‚˜μ˜ 객체둜 λ°˜ν™˜
public class TestInvocationHandler implements InvocationHandler {

    private TargetInterface target; //타깃 였브젝트λ₯Ό 멀버 λ³€μˆ˜λ‘œ μœ μ§€ν•΄μ•Ό ν•©λ‹ˆλ‹€.

    public TestInvocationHandler(TargetInterface target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        log.info("ν”„λ‘μ‹œ λΆ€κ°€κΈ°λŠ₯ μ‹œμž‘");  //여기에 λΆ€κ°€κΈ°λŠ₯ λ‘œμ§μ„ μΆ”κ°€
        Object result = method.invoke(target, args);  //μ‹€μ œ 타깃 였브젝트둜 μš”μ²­μ„ μœ„μž„ν•©λ‹ˆλ‹€.
        log.info("ν”„λ‘μ‹œ λΆ€κ°€κΈ°λŠ₯ μ’…λ£Œ");  //여기에 λΆ€κ°€κΈ°λŠ₯ λ‘œμ§μ„ μΆ”κ°€

        return result;
    }
}
@Test
void testInvocationHandler(){
    TargetInterface target = new TargetImpl();
    TestInvocationHandler invocationHandler = new TestInvocationHandler(target);

    //ν”„λ‘μ‹œ 생성
    TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(TargetInterface.class.getClassLoader(), new Class[]{TargetInterface.class}, invocationHandler);

    //ν”„λ‘μ‹œ μ‹€ν–‰
    proxy.run();
    log.info("proxy = {}", proxy.getClass());

}

μƒˆλ‘œμš΄ 타깃에 기쑴의 ν”„λ‘μ‹œλ₯Ό μ μš©ν•˜λ €λ©΄ μƒˆλ‘œμš΄ νƒ€κΉƒμ˜ μΈν„°νŽ˜μ΄μŠ€μ™€ μΌμΉ˜ν•˜λŠ” ν”„λ‘μ‹œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ§Œλ“€μ–΄μ•Ό ν•˜λŠ” 뢀뢄을 InvocationHandlerλ₯Ό μ‚¬μš©ν•΄ λ™μ μœΌλ‘œ 생성해 ν•΄κ²°

 

CGLIB

CGLIB은 λ°”μ΄νŠΈ μ½”λ“œλ₯Ό μ‘°μž‘ν•˜μ—¬ λ™μ μœΌλ‘œ 클래슀 μƒμ„±ν•΄μ£ΌλŠ” 라이브러리

Enhancer enhancer = new Enhancer();
         enhancer.setSuperclass(MemberService.class); // 타깃 클래슀
         enhancer.setCallback(MethodInterceptor);     // Handler
Object proxy = enhancer.create(); // Proxy 생성

μΈν„°νŽ˜μ΄μŠ€ 없이 concrete 클래슀만 μ‘΄μž¬ν•΄λ„ ν”„λ‘μ‹œ 생성 κ°€λŠ₯

CGLIB은 MethodIntercepter 톡해 κ΅¬ν˜„λ¨, InvocationHandler와 맀우 μœ μ‚¬

μ„±λŠ₯적으둜 CGLIB이 μ’‹μŒ

CGLB은 타깃에 λŒ€ν•œ 정보λ₯Ό 제곡 λ°›μ•„ λ°”μ΄νŠΈ μ½”λ“œλ₯Ό μ‘°μž‘ν•΄μ„œ Proxyλ₯Ό μƒμ„±ν•˜κΈ° λ•Œλ¬Έμ— 이후 ν˜ΈμΆœμ‹œμ—λŠ” μ‘°μž‘λœ λ°”μ΄νŠΈ μ½”λ“œλ₯Ό μž¬μ‚¬μš©ν•˜μ—¬ μ„±λŠ₯적으둜 μ’‹λ‹€.

ν•˜μ§€λ§Œ 초기 Springμ—μ„œλŠ” JDK 방식을 μ‚¬μš©ν–ˆμ—ˆμŒ

  • net.sf.cglib.proxy.Enhancer μ˜μ‘΄μ„± μΆ”κ°€
  • default μƒμ„±μž
  • νƒ€κΉƒμ˜ μƒμ„±μž 두 번 호좜

같은 문제점이 CGLIB에 μ‘΄μž¬ν–ˆκΈ° λ•Œλ¬Έμ—

But, λŒ€λΆ€λΆ„ κ°œμ„ λ˜μ–΄ ν˜„μž¬ μŠ€ν”„λ§μ˜ λ‹€μ΄λ‚˜λ―Ή ν”„λ‘μ‹œλŠ” CGLIB으둜 κ΅¬ν˜„λ¨

 

μŠ€ν”„λ§ AOP의 Proxy λ™μž‘

λŸ°νƒ€μž„ μœ„λΉ™(Runtime Weaving)

  1. Spring AOPλŠ” 호좜 μ‹œμ μ— IoC μ»¨ν…Œμ΄λ„ˆμ— μ˜ν•΄ Proxy Bean을 생성
    1. μΈν„°νŽ˜μ΄μŠ€ μœ λ¬΄μ— 따라 방식 선택(JDK / CGLIB)
  2. λ™μ μœΌλ‘œ μƒμ„±λœ Proxy Bean은 νƒ€κΉƒμ˜ λ©”μ†Œλ“œκ°€ ν˜ΈμΆœλ˜λŠ” μ‹œμ μ— λΆ€κ°€ κΈ°λŠ₯을 μΆ”κ°€ν•  λ©”μ†Œλ“œλ₯Ό νŒλ‹¨ 및 μ£Όμž…

 

λ‹€μ΄λ‚˜λ―Ή ν”„λ‘μ‹œμ˜ 단점

  • μΈν„°νŽ˜μ΄μŠ€μ˜ μœ λ¬΄μ— 따라 JDK 동적 ν”„λ‘μ‹œ, CGLIB으둜 각각 κ΅¬ν˜„ν•΄μ•Ό ν–ˆμŒ
  • λ‹€μ΄λ‚˜λ―Ή ν”„λ‘μ‹œλ₯Ό κ΅¬ν˜„ν•˜λŠ” InvocationHandler와 MethodInterceptor에 타깃 μ˜€λΈŒμ νŠΈκ°€ μ‘΄μž¬ν•΄μ•Ό 함

 

ν”„λ‘μ‹œ νŒ©ν† λ¦¬λž€?

λ‹€μ΄λ‚˜λ―Ή ν”„λ‘μ‹œμ˜ 단점듀을 ν•΄κ²°ν•˜κ³ μž μœ„μ˜ μΈν„°νŽ˜μ΄μŠ€μ˜ μœ λ¬΄μ— 따라 ν”„λ‘μ‹œλ₯Ό μƒμ„±ν•΄μ£ΌλŠ” νŒ©ν† λ¦¬

즉, κΈ°μ‘΄ λ‹€μ΄λ‚˜λ―Ή ν”„λ‘μ‹œμ— νŒ©ν† λ¦¬ νŒ¨ν„΄ 적용

 

 

Advidsor, Advice

πŸ’‘ Advisor = Advice + Pointcut

Advice: 타깃 μ˜€λΈŒμ νŠΈμ— μ μš©ν•  λΆ€κ°€κΈ°λŠ₯을 담은 였브젝트

Pointcut: λΆ€κ°€ κΈ°λŠ₯을 μ μš©ν•  λŒ€μƒμΈμ§€ νŒλ³„ν•˜λŠ” 필터링 둜직

 

ν”„λ‘μ‹œ νŒ©ν† λ¦¬μ˜ 단점

  • ν”„λ‘μ‹œλ₯Ό μ μš©ν•  μŠ€ν”„λ§ Bean의 갯수만큼 ν”„λ‘μ‹œλ₯Ό μƒμ„±ν•΄μ„œ Bean으둜 등둝해주어야 함
  • μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ”μœΌλ‘œ λ“±λ‘λœ μŠ€ν”„λ§ Beanμ—λŠ” 적용 λΆˆκ°€λŠ₯

 

빈 ν›„μ²˜λ¦¬κΈ°(Bean Post Processor)

ν”„λ‘μ‹œ νŒ©ν† λ¦¬κ°€ 가지고 μžˆλŠ” 단점을 ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λΉˆμ„ λ“±λ‘ν•˜κΈ° 전에 μ›ν•˜λŠ”λŒ€λ‘œ μ‘°μž‘ν•  수 μžˆλŠ” **빈 ν›„μ²˜λ¦¬κΈ°(Bean Post Processor)**λ₯Ό μ‚¬μš©

μ°Έκ³ 

[Spring] ν”„λ‘μ‹œμ™€ λ””μžμΈνŒ¨ν„΄ (tistory.com)

[Spring] 닀이내믹 ν”„λ‘μ‹œ(DynamicProxy) (tistory.com)`

[Spring] ν”„λ‘μ‹œ νŒ©ν† λ¦¬(ProxyFactory) (tistory.com)

JDK Dynamic Proxy와 CGLIB의 차이점은 λ¬΄μ—‡μΌκΉŒ? | Moon`s Development Blog (gmoon92.github.io)

λ°˜μ‘ν˜•