반응형
반응형

1. 개요

 - 업무를 하던 도중 특정 로직에서 Enhancer, MethodInterceptor이라는 객체를 사용하고 있었다. 로직을 분석해봐도 이해가 잘 가지 않았던 구조였기 때문에 공부의 필요성을 느껴 공부 후 내용을 정리한다.


2. Enhancer란?

 - Enhancer의 사전적 의미는 기능을 높이는 것, 증진시키는 것을 의미한다. 이를 프록시에 대입하여 생각해보니 프록시 객체는 다양한 객체를 호출할 수 있기때문에 기능이 증진된다는 것과 비슷한 맥락을 갖는 것 같다.

 - 로직적으로 이 객체는 프록시 객체를 생성하는 역할을 한다.


3. Enhancer를 사용한 프록시 객체 생성

 - Enhancer 객체를 사용하여 프록시 객체를 생성하는 예제이다.

 

3.1) EnhancerTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.ssk.simpany.test.methodInterceptorTest;
 
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.NoOp;
 
public class EnhancerTest {
 
    public static void main(String[] args) {
        
        Enhancer enhancer = new Enhancer(); //Enhancer 객체 생성
        
        enhancer.setSuperclass(MyService.class); // 타켓 클래스 지정
        enhancer.setCallback(NoOp.INSTANCE);      // 옵션  (NoOp, MethodIntetceptor 등)
        Object targetObj = enhancer.create();     // 프록시 객체 생성
        
        if(targetObj instanceof MyService){           // targetObj 객체가 MyService로 형 변환이 가능한지
            //형변환 가능.
            MyService myService = (MyService)targetObj; //형변환
            myService.myServiceMethod("test");
        }
    }
}
 
cs

 

3.2) MyService.java

1
2
3
4
5
6
7
8
9
package com.ssk.simpany.test.methodInterceptorTest;
 
public class MyService {
 
    public String myServiceMethod(String a){
        System.out.println("call my ServiceMethod is "+a);
        return a;
    }
}
cs

 

- EnhancerTest 클래스에서 프록시 객체를 생성할 수 있는 Enhancer 객체를 생성하고 setSuperclass 메서드를 사용하여 타겟 클래스를 지정한다.

- setCallback 메서드에는 NoOp.INSTANCE(NoOption)을 입력했는데, 말 그대로 옵션이 없는 프록시객체로 설정하기 위함이다.

- NoOp로 설정하면 단순히 프록시 객체를 통해 타겟 클래스만 호출하는 것이고, MethodInterceptor로 설정하면 타겟 클래스 호출 전 후로 로직을 넣거나 매개변수를 변경하는 등의 작업이 가능하다. 후자의 경우도 다뤄보도록 할 예정이다.

- enhancer.create() 메서드를 호출하면 마침내 프록시 객체를 생성하게 된다.

- Object 형으로 반환되게 되지만 내부적으로는 앞서 설정한 타켓 클래스이기 때문에 instanceof를 사용하여 형 변환 또한 가능함을 확인할 수 있다. 결과적으로 enhance 에서 생성한 프록시 객체를 통해 MyService객체를 호출한다.

 

3.3) 결과

프록시 객체를 통한 MyService 호출 결과

 

  사실 위 예제를 직접 작성했을땐 '그냥 객체 생성하고 호출하면 될것이지 왜 프록시까지 굳이 생성해서 호출하는걸까?' 라는 생각과 함께 비지니스 로직에 의해 동적으로 호출되어야 할 객체가 있다면 이를 사용했을 때 어느정도 효과를 볼 수 있지 않을까라는 생각도 들었다. 타겟 객체를 생성하는 게 아닌 호출하는 것이므로 메모리적으로도 좋지않을까? 혹시 누군가 알고계시다면 댓글로 달아주시길 부탁드리와요..


4. MethodInterceptor란?

 프록시 객체의 콜백 기능 중 하나로, 프록시 객체에서 타겟 객체를 호출하기 전, 후로 비지니스 로직을 추가하거나, 타겟 클래스가 아닌 다른 객체의 메소드를 호출하거나, 인자 값을 변경할 수 있다.

 구현 방법은 커스텀한 클래스에 MethodInterceptor 인터페이스를 상속받은 후 intercept 메서드를 오버라이드 한다.

 

4.1) MethodInterceptorTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.ssk.simpany.test.methodInterceptorTest;
 
import java.lang.reflect.Method;
 
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
 
public class MethodInterceptorTest implements MethodInterceptor{
 
    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("before target Proxy");
        Object returnValue = methodProxy.invokeSuper(object, args);
        System.out.println("after target Proxy");
        return returnValue;
    }
}
cs

 

4.2) Main.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.ssk.simpany.test.methodInterceptorTest;
 
import org.springframework.cglib.proxy.Enhancer;
 
public class Main {
 
    public static void main(String[] args) {
        
        Enhancer enhancer = new Enhancer();     // enhancer 객체 생성
        enhancer.setSuperclass(MyService.class);// 타겟 클래스 지정
        enhancer.setCallback(new MethodInterceptorTest()); //콜백으로 MethodInterceptorTest 객체 설정
        Object targetObj = enhancer.create();    //프록시 생성
        MyService myService = (MyService)targetObj; //형변환
        myService.myServiceMethod("test"); //프록시 객체로 메서드 호출
    }
}
 
cs

- MethodInterceptor를 사용하여 아주 간단한 프록시 객체를 구현했다. 기존과 다른 건 MethodInterceptorTest 클래스를 구현한 후 callback 으로 설정했다는 점이다.

- 이 설정으로 인해 프록시 객체로 메서드를 호출하면 MethodInterceptorTest 클래스의 intercept 메서드가 호출되게 되어 before target Proxy 문자열 호출 후 타겟 클래스가 실행됨을 확인할 수 있다.

참고로 methodProxy.invokeSuper의 리턴 값은 타겟 객체의 리턴 값이다.

 

4.3) 결과

MethodInterceptor를 적용한 프록시 객체 호출 결과


5. 마치며

 지금은 단순히 enhancer와 MethodInterceptor를 이용해 프록시를 구현하는 것만 해보아서 그런지 느낌만 알 뿐, 아직도 머릿속에서 정리가 되지 않은 느낌이다. 또한 어떻게 활용되는지도 크게 와닿지 않는다. 다음 게시글로 이 방식을 활용하여 다양한 예제를 만들어보도록 하겠다.

반응형

+ Recent posts