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) 결과
사실 위 예제를 직접 작성했을땐 '그냥 객체 생성하고 호출하면 될것이지 왜 프록시까지 굳이 생성해서 호출하는걸까?' 라는 생각과 함께 비지니스 로직에 의해 동적으로 호출되어야 할 객체가 있다면 이를 사용했을 때 어느정도 효과를 볼 수 있지 않을까라는 생각도 들었다. 타겟 객체를 생성하는 게 아닌 호출하는 것이므로 메모리적으로도 좋지않을까? 혹시 누군가 알고계시다면 댓글로 달아주시길 부탁드리와요..
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) 결과
5. 마치며
지금은 단순히 enhancer와 MethodInterceptor를 이용해 프록시를 구현하는 것만 해보아서 그런지 느낌만 알 뿐, 아직도 머릿속에서 정리가 되지 않은 느낌이다. 또한 어떻게 활용되는지도 크게 와닿지 않는다. 다음 게시글로 이 방식을 활용하여 다양한 예제를 만들어보도록 하겠다.
'백엔드 > Spring' 카테고리의 다른 글
[Spring] 스프링 AOP / 용어 / 빈 후처리기 (0) | 2023.07.25 |
---|---|
[Spring] 다이내믹 프록시 / 데코레이터 패턴 / 예제 / 테스트 (0) | 2023.07.05 |
[Spring] ContextLoaderListener 란? RootApplicationContext과 WebApplicationContext란? (1) | 2021.03.29 |
@RestController, @Controller 어노테이션의 차이 (2) | 2021.01.22 |
@RunWith, @ContextConfiguration 어노테이션 (0) | 2021.01.21 |