이전 시간에는 프록시 패턴에 대해서 알아보았다.
프록시 패턴을 이용할 시 서브젝트,리얼 서브젝트,프록시 등 서비스를 구현할 때마다 많은 클래스와 인터페이스를 만들어야하고
컴파일 시에만 구현할 수 있다는 단점이 있었다.
오늘은 이걸 보완한 다이나믹 프록시에 대해서 알아보려고 한다.
먼저, 다이나믹 프록시란 무엇일까?
다이나믹 프록시란 런타임에 특정 인터페이들을 구현하는 클래스 또는 인스턴스를 만드는 기술이다.
여기서 중요한 점은 런타임이라는 말과 클래스 또는 인스턴스를 만드는 기술이라는 것이 다이나믹 프록시의 핵심인것 같다
다이나믹 프록시는 우리가 배웠던 리플렉션 구현 되었고 그것을 이용해 다이나믹 프록시 패턴은 만들어 보려고 한다.
먼저 인스턴스를 만드는 방법이다.
DynamicProxyTest.class
public class DynamicProxyTest {
ProxyBookService proxyBookService = (ProxyBookService) Proxy.newProxyInstance(ProxyBookService.class.getClassLoader(), new Class[]{ProxyBookService.class}, new InvocationHandler() {
final ProxyBookService proxyBookService = new DefaultProxyBookService();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("rent")) {
System.out.println("dynamic proxy start");
Object invoke = method.invoke(proxyBookService, args);
System.out.println("dynamic proxy end");
return invoke;
}
return method.invoke(proxyBookService, args);
}
});
@Test
public void di() {
ProxyBook proxyBook = new ProxyBook();
proxyBook.setTitle("kibong");
proxyBookService.rent(proxyBook);
proxyBookService.returnBook(proxyBook);
}
}
출력
dynamic proxy start
rent Book -> kibong
dynamic proxy end
return Book -> kibong
해당 코드를 보면
proxyBookService를 호출할 때 Proxy.newProxyInstance를 통해 ProxyBookService 인스턴스를 만들 수 있고
해당 인스턴스를 사용함으로 써 우리는 다이나믹 프록시를 사용할 수 있다.
Proxy.newProxyInstance는 3개의 인자를 받는데
첫번째는 서브젝트의 클래스로더, 두번째는 서브젝트의 클래스, 세번째는 InvocationHandler이다.
InvocationHandler에서는 우리가 사용했던 리플렉션을 통해 invoke를 생성해주고 리턴해 준다.
메소드 명마다 프록시의 행동을 다르게 할 수 있는 그런 다이나믹 프록시가 만들어졌다.
우리가 사용했던 프록시 패턴보다 확실히 코드의 재사용성이 높아졌다.
하지만 이 또한 문제점이 있는데
첫번째는 서비스가 좀 커지면 되게 코드가 복잡해진다는 단점이 있고
두번째는 파라미터에 인터페이스 밖에 넣을 수 없다는 단점이 있다.
그럼 인터페이스가 없는 클래스 같은 경우에는 어떻게 사용해야 할까!?
그것을 cglib이라는 라이브러리를 통해 해결해보려고 한다.
DynamicClassProxyTest.class
public class DynamicClassProxyTest {
@Test
public void di() {
MethodInterceptor handler = new MethodInterceptor() {
final DefaultProxyBookService defaultProxyBookService = new DefaultProxyBookService();
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println(" ");
System.out.println("메소드 이름 : " + method.getName());
//argument 출력
for (Object arg : args) {
System.out.println(arg);
}
//rent의 경우에만 Start End 붙여줌
if (method.getName().equals("rent")) {
System.out.println("Dynamic Proxy Class Start");
Object invoke = method.invoke(defaultProxyBookService, args);
System.out.println("Dynamic Proxy Class End");
return invoke;
}
return method.invoke(defaultProxyBookService, args);
}
};
//Cglib Enhancer 생성
DefaultProxyBookService defaultProxyBookService = (DefaultProxyBookService) Enhancer.create(DefaultProxyBookService.class, handler);
ProxyBook proxyBook = new ProxyBook();
proxyBook.setTitle("kibong");
defaultProxyBookService.rent(proxyBook);
defaultProxyBookService.returnBook(proxyBook);
System.out.println(" ");
}
}
위에 방법이 클래스로 사용하는 방법인데
먼저 CGLib의 Ehancer.create()로 런타임 시 인스턴스를 반환 하도록 한다.
첫번째 인자는 리턴 값, 두번째는 핸들러이다.
그 다음 핸들러를 만들어 주고 다이나믹 프록시 패턴으로 구현한다.
이렇게 두고 보면 코드 자체가 인터페이스와 그렇게 많이 달라지지는 않았다.
하지만 인터페이스가 없어도 클래스로 다이나믹 프록시 패턴을 구현할 수 있다는 점은 굉장히 흥미롭다.
Spring AOP에서 해당 패턴을 이용중이라고 하는데 나중에 토비의 Spring 책을 읽어보고 조금 더 학습해 보고 싶어지는 마음이 들어
학습하게 된다면 블로그에 포스팅 하도록 하겠습니다.
감사합니다.
https://github.com/kibongcoders/java_study
GitHub - kibongcoders/java_study
Contribute to kibongcoders/java_study development by creating an account on GitHub.
github.com
출처 : https://www.inflearn.com/course/the-java-code-manipulation/dashboard
더 자바, 코드를 조작하는 다양한 방법 - 인프런 | 강의
여러분이 사용하고 있는 많은 자바 라이브러리와 프레임워크가 "어떻게" 이런 기능을 제공할 지 궁금한적 있으신가요? 이번 강좌를 통해 자바가 제공하는 다양한 코드 또는 객체를 조작하는 방
www.inflearn.com
'Java > 더 자바, "코드를 조작하는 다양한 방법"' 카테고리의 다른 글
애노테이션 프로세서 Annotation Processor (0) | 2023.02.01 |
---|---|
프록시 Proxy 프록시 패턴 (0) | 2023.01.15 |
리플렉션 Reflection (0) | 2023.01.11 |
JVM 이해하기 (0) | 2022.12.21 |