JDK 동적 프록시와 CGLIB(Code Generate Library) 차이
- CGLIB는 바이트코드를 조작해서 동적으로 클래스를 생성하는 기술을 제공하는 라이브러리
- 인터페이스가 있어야만 사용할 수 있는 JDK 동적 프록시와 달리 CGLIB를 사용하면 구체 클래스만 가지고도 동적 프록시를 만들어낼 수 있다.
- CGLIB는 외부 라이브러리인데, 스프링 프레임워크가 스프링 내부 소스 코드에 포함하여 별도의 외부 라이브러리를 추가하지 않아도 사용할 수 있다.
리플렉션(reflection)
JDK 동적 프록시를 이해하기 위해서는 먼저 자바의 리플렉션 기술을 이해해야 한다.
리플렉션 기술을 사용하면 클래스나 메서드의 메타정보를 동적으로 획득하고, 코드를 동적으로 호출할 수 있다.
일반적인 경우, 대부분의 로직은 공통이지만 중간에 인스턴스에서 호출하는 메서드가 다를 경우 공통화 하기 어렵다.
(ex : target.callA(), target.callB();)
이럴때 클레스나 메서드의 메타정보를 사용할 수 있는 리플렉션 기술을 사용하여, 컴파일 시점이 아닌 런타임시점에 호출하는 메서드를 동적으로 바꿀 수 있다.
아래 코드의 dynamicCall() 메서드 부분이 리플렉션 기술을 이용하여 공통화 한 결과.
주의사항 :
접근 지시자를 무시할 수 있는 문제가 발생할 수 있는 가능성이 존재한다.
또한 리플렉션을 사용하면 클래스와 메서드의 메타정보를 사용해서 애플리케이션을 동적으로 유연하게 만들 수 있다.
하지만 리플렉션 기술은 런타임에 동작하기 때문에 컴파일 시점에 오류를 잡을 수 없는 문제가 있다.
getMethod("callXXXX") 처럼 존재하지 않는 메서드의 이름을 실수로 넣었을 때, 컴파일 시점에 에러가 발생하지 않고 따라서 런타임 시점에서야 오류를 발견할 수 있는 문제가 있다.
그래서 컴파일 시점에 오류를 잡을 수 있도록 가급적이면 리플렉션 기술은 사용하면 안된다.
@Slf4j
public class ReflectionTest {
@Test
void reflection0(){
Hello target = new Hello();
// 공통 로직 1 시작
log.info("start");
String result1 = target.callA();
log.info("result={}", result1);
// 공통 로직 1 종료
// 공통 로직 2 시작
log.info("start");
String result2 = target.callB();
log.info("result={}", result2);
// 공통 로직 2 종료
}
@Test
void reflection1() throws Exception{
// 클래스 정보
Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");
Hello target = new Hello();
//callA 메서드 정보
Method methodCallA = classHello.getMethod("callA");
dynamicCall(methodCallA, target);
//callB 메서드 정보
Method methodCallB = classHello.getMethod("callB");
dynamicCall(methodCallB, target);
}
private void dynamicCall(Method method, Object target) throws Exception{
log.info("start");
Object result1 = method.invoke(target);
log.info("result={}", result1);
}
@Slf4j
static class Hello {
public String callA(){
log.info("callA");
return "A";
}
public String callB(){
log.info("callB");
return "B";
}
}
}
동적 프록시
댓글