이전에 살펴보았던 프록시 패턴에서는 원하는 클래스나 인터페이스마다 프록시 클래스를 만들어주어야 하는 문제가 있었다. 100곳에서 프록시를 사용한다면 프록시 클래스를 100개 만들어주어야 한다는 것이다. 이 문제가 가장 큰 걸림돌이었는데 JDK 동적 프록시를 이용하면 이 문제를 해결할 수 있다. 참고로 JDK 동적 프록시는 자바 리플렉션을 사용하므로 클래스 메타 데이터를 가지고 동작한다.
동적 프록시 사용하기
이번에 구현할 내용은 원하는 문자열을 ===========로 감싸서 화면에 출력하는 것이다.
public interface MyTextInterface {
void print(String text);
}
원하는 내용을 받을 메서드를 정의해 놓았다.
@Slf4j
public class MyTextImpl implements MyTextInterface {
@Override
public void print(String text) {
log.info(text);
}
}
위 인터페이스를 구현한 클래스이다.
@Slf4j
@RequiredArgsConstructor
public class WrapHandler implements InvocationHandler {
private final Object 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;
}
}
이 부분이 이전에 프록시 패턴을 사용할 때 작성했던 프록시 클래스 대신 작성하는 부분이다. 가운데에서 어느 MyTextInterface 구현 클래스로 출력하든 위 핸들러 클래스 하나만 작성하면 뭐든지 ==========로 감쌀 수 있다.
프록시 클래스는 내부에 실제로 호출할 부분을 참조하는 변수를 가지고 있어야 하기 때문에 target이라는 이름으로 내부에 오게 될 실제 로직 부분 변수를 선언해 주었다. 나중에 WrapHandler 인스턴스 생성 시에 생성자에서 할당해 주게 된다.
public class WrapTest {
@Test
void wrapTextTest() {
MyTextInterface target = new MyTextImpl();
WrapHandler handler = new WrapHandler(target);
MyTextInterface proxy = (MyTextInterface) Proxy.newProxyInstance(MyTextInterface.class.getClassLoader(),
new Class[]{MyTextInterface.class}, handler);
proxy.print("안녕하세요 ^^b");
}
}
새로 프록시 인스턴스를 만들면 Object로 반환되기 때문에 Object의 메서드밖에 사용할 수 없다. 그렇기 때문에 다운캐스팅으로 이전에 정의해 놨던 MyTextInterface로 형변환하여 사용하여야 한다.
이번 동적 프록시의 사용 목적이 하나의 프록시 클래스만 정의해서 해당 클래스를 공통적으로 사용하는 것이기 때문에 다음과 같은 클래스를 하나 더 만들었다.
@Slf4j
public class MyTextImpl2 implements MyTextInterface {
@Override
public void print(String text) {
log.info("!!!!!!!" + text + "!!!!!!!");
}
}
이번에는 앞뒤로 !!!!!!!가 붙도록 정의했다.
public class WrapTest {
@Test
void wrapTextTest() {
MyTextInterface target = new MyTextImpl();
WrapHandler handler = new WrapHandler(target);
MyTextInterface proxy = (MyTextInterface) Proxy.newProxyInstance(MyTextInterface.class.getClassLoader(),
new Class[]{MyTextInterface.class}, handler);
proxy.print("안녕하세요 ^^b");
// --------------------------
handler = new WrapHandler(new MyTextImpl2());
proxy = (MyTextInterface) Proxy.newProxyInstance(MyTextInterface.class.getClassLoader(),
new Class[]{MyTextInterface.class}, handler);
proxy.print("안녕하세요 ^^b");
}
}
이전의 WrapHandler 클래스를 그대로 사용하여 내부의 멤버 변수만 다르게 생성해 주고 다시 실행했는데 두 번째도 원하던 대로 잘 출력되는 것을 알 수 있었다.
이렇게 동적 프록시를 사용하면 여러 프록시 클래스를 만들지 않아도 돼서 번거롭지 않다는 장점이 있다.
'공부 > Java' 카테고리의 다른 글
[Java] 문자열 그대로 클래스로 만들어 컴파일 후 실행하기 (0) | 2024.09.28 |
---|---|
[Java] 퀵정렬 (QuickSort) 구현하기 (0) | 2024.09.24 |
[Java][Spring] ThreadLocal 사용하기 (0) | 2024.07.13 |
[Java] 람다식 (Lambda expression) (0) | 2024.02.23 |
[Java] 생성자 (Constructor) (0) | 2024.02.22 |