리플렉션 프록시 패턴은 객체의 동작을 중간에 가로채서 추가적인 동작을 수행하거나 변경하는 디자인 패턴입니다. Java에서는 리플렉션을 통해 클래스, 메서드, 필드 등에 접근할 수 있으며, 이러한 기능을 이용하여 프록시 객체를 구현할 수 있습니다.
이번 예제에서는 Javassist를 사용하여 리플렉션 프록시 객체를 구현하는 방법을 알아보겠습니다. Javassist는 Java 클래스 파일을 생성, 수정, 분석하는 데 사용되는 라이브러리입니다.
Javassist 설치
Javassist를 사용하기 위해서는 먼저 Javassist 라이브러리를 다운로드하고 프로젝트에 추가해야 합니다. Maven을 사용하는 경우, pom.xml 파일에 다음 의존성을 추가합니다:
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
</dependencies>
Maven을 사용하지 않고 직접 라이브러리를 추가하는 경우, 다운로드 한 Javassist JAR 파일을 프로젝트의 빌드 경로에 추가합니다.
프록시 객체 생성
- 프록시 객체를 생성할 인터페이스를 정의합니다. 예를 들어, 다음과 같은
UserService
인터페이스를 가정해봅시다:
public interface UserService {
void addUser(String username);
}
UserService
인터페이스를 구현하는 클래스에 대한 프록시 객체를 생성합니다. 다음은ProxyUserService
클래스의 예시입니다:
public class ProxyUserService implements UserService {
private UserService target;
public ProxyUserService(UserService target) {
this.target = target;
}
@Override
public void addUser(String username) {
// 추가적인 동작을 수행하고 원본 메서드를 호출합니다
System.out.println("Before adding user");
target.addUser(username);
System.out.println("After adding user");
}
}
- 메인 메서드에서 프록시 객체를 사용하도록 설정합니다:
public class Main {
public static void main(String[] args) {
// 원본 객체 생성
UserService userService = new UserServiceImpl();
// 프록시 객체 생성
ProxyUserService proxyUserService = new ProxyUserService(userService);
// 프록시 객체를 사용하여 메서드 호출
proxyUserService.addUser("john");
}
}
Javassist를 사용한 프록시 객체 생성
이제 Javassist를 사용하여 동일한 리플렉션 프록시 패턴을 구현해보겠습니다. Javassist를 사용하면 런타임에 클래스를 생성하고 수정할 수 있습니다.
import javassist.*;
public class JavassistProxy {
public static void main(String[] args) throws Exception {
// 클래스 파일을 생성할 클래스 객체
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass("ProxyUserService");
// UserService 인터페이스 상속
ctClass.addInterface(classPool.get("UserService"));
// 원본 UserService 객체를 가리키는 필드 추가
CtField targetField = CtField.make("private UserService target;", ctClass);
ctClass.addField(targetField);
// 프록시 객체 생성자 추가
CtConstructor constructor = CtNewConstructor.make(
"public ProxyUserService(UserService target) { this.target = target; }",
ctClass);
ctClass.addConstructor(constructor);
// addUser 메서드 추가
CtMethod addUserMethod = CtNewMethod.make(
"public void addUser(String username) { " +
"System.out.println(\"Before adding user\"); " +
"target.addUser(username); " +
"System.out.println(\"After adding user\"); }",
ctClass);
ctClass.addMethod(addUserMethod);
// 클래스 파일로 저장
ctClass.writeFile();
}
}
위의 코드에서는 makeClass()
메서드를 사용하여 ProxyUserService
클래스를 생성하고, make()
메서드를 사용하여 필드와 메서드를 추가합니다. 필드 및 메서드의 내용은 문자열로 작성되며, 이는 자바 소스 코드와 동일한 형식으로 작성됩니다.
프록시 클래스를 생성한 후, 해당 클래스의 인스턴스를 생성하여 사용하면 됩니다.
결론
이렇게 Javassist를 사용하여 리플렉션 프록시 패턴을 구현할 수 있습니다. Javassist는 동적으로 클래스를 생성하고 수정할 수 있는 강력한 도구입니다. 프록시 패턴은 객체의 동작을 변경하거나 확장하기 위해 많이 사용되며, 리플렉션을 통한 프록시 구현은 유연성과 확장성을 제공합니다.