Java에서는 동적으로 클래스를 생성하여 프록시 패턴을 구현할 수 있습니다. 이를 위해 Javassist라는 라이브러리를 사용할 수 있습니다. Javassist는 바이트 코드를 조작할 수 있는 강력한 도구로써, 클래스 파일을 직접 작성하지 않고도 프록시 클래스를 생성할 수 있습니다.
Javassist 라이브러리 추가
먼저, 프로젝트의 의존성에 Javassist 라이브러리를 추가해야 합니다. Maven을 사용한다면 pom.xml
파일에 다음과 같이 의존성을 추가해주세요:
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
</dependencies>
Gradle을 사용한다면 build.gradle
파일에 다음과 같이 의존성을 추가해주세요:
dependencies {
implementation 'org.javassist:javassist:3.27.0-GA'
}
프록시 클래스 생성하기
다음은 Javassist를 사용하여 프록시 클래스를 생성하는 간단한 예제입니다. 예를 들어, UserService
인터페이스의 구현체를 프록시로 감싸는 프록시 클래스를 생성해봅시다.
import javassist.*;
public class ProxyGenerator {
public static <T> T createProxy(Class<T> interfaceClass, T target) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass(interfaceClass.getName() + "Proxy");
// 인터페이스 구현
ctClass.addInterface(pool.get(interfaceClass.getName()));
// 필드 추가
ctClass.addField(CtField.make("private " + interfaceClass.getName() + " target;", ctClass));
// 생성자 추가
CtConstructor constructor = new CtConstructor(new CtClass[]{pool.get(interfaceClass.getName())}, ctClass);
constructor.setBody("{this.target = $1;}");
ctClass.addConstructor(constructor);
// 메소드 추가
for (CtMethod method : pool.get(interfaceClass.getName()).getDeclaredMethods()) {
ctClass.addMethod(CtNewMethod.wrapped(method, "{return target." + method.getName() + "($$);}", ctClass));
}
// 프록시 객체 생성
Class<?> proxyClass = ctClass.toClass();
return (T) proxyClass.getConstructor(interfaceClass).newInstance(target);
}
}
위의 예제 코드에서는 createProxy
메소드를 통해 프록시 클래스를 생성합니다. 이 메소드는 인터페이스 클래스와 대상 객체를 인자로 받아 프록시 객체를 반환합니다. 이 때, 프록시 객체는 대상 객체에 정의된 모든 메소드를 호출하고 그 결과를 반환합니다.
사용 예제
아래는 위에서 생성한 ProxyGenerator
클래스를 사용하는 예제입니다.
public interface UserService {
void registerUser(User user);
User getUser(String username);
}
public class UserServiceImpl implements UserService {
public void registerUser(User user) {
// 사용자 등록 로직
}
public User getUser(String username) {
// 사용자 조회 로직
return null;
}
}
public class Main {
public static void main(String[] args) {
// 프록시 클래스 생성
try {
UserService userService = new UserServiceImpl();
UserService proxy = ProxyGenerator.createProxy(UserService.class, userService);
// 프록시 메소드 호출
User user = new User("John");
proxy.registerUser(user);
User foundUser = proxy.getUser("John");
// 프록시 결과 확인
System.out.println("Found user: " + foundUser.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
위의 예제에서는 UserServiceImpl
클래스를 UserService
인터페이스를 구현하는 프록시로 감싸는 프록시 객체를 생성합니다. 프록시 객체를 사용하여 메소드를 호출하고, 호출 결과를 확인합니다.
결론
Javassist를 사용하면 동적으로 프록시 클래스를 생성할 수 있습니다. 이를 활용하여 프록시 패턴을 구현하면, 코드의 재사용성과 유지보수성을 향상시킬 수 있습니다. Javassist의 다양한 기능을 활용하여 복잡한 클래스 변환 및 동적 코드 생성 기능을 구현할 수도 있습니다.