최근 수행중인 프로젝트에서 JPA의 구현체로 하이버네이트를 사용하고 있다. 매핑 설정에 따라 다르겠지만, 이런 JPA 구현체들은 Lazy 로딩을 구현하기 위해 프록시 객체를 사용하고 있는데, 이와 관련해서 한 가지 주의해야 할 것이 있다. 그것은 바로 this를 리턴하는 메서드와 관련된 것이다.
현재 수행중인 프로젝트에는 상속을 매핑한 코드가 있다. 예를 들면 다음과 같은 구조이다.
public class Content { ... 최상위 }
public class VideoContent extends Content { .... }
public class MovieContent extends VideoContent { ... }
public class BookContent extends Content { ... }
그리고, 특정 클래스과 다음과 같이 Content를 *-to-One의 형식으로 레퍼런스하고 있다.
public class Order {
private Content content;
public Content getContent() {
return content;
}
}
Order 객체를 구한 뒤에 Content 객체에 접근해야 하는 경우가 있으며, 특히 Content 타입이 아닌 실제 타입에 접근해야 할 때가 있다. 이런 경우에 아주 단순하게 생각하면 다음과 같이 Content에 자기 자신을 리턴하는 코드를 넣는 방법을 생각해 볼 수 있을 것이다.
public Content {
public Content self() {
return this; // 자기 자신을 리턴
}
}
Order o = getOrder(xxx);
Content proxy = o.getContent(); // Content는 프록시
Content real = proxy.self(); // self() 메서드는 자기 자신을 리턴하므로 프록시가 아닌 실제 대상 객체???
if (real instanceof MovieContent) { // 하지만, real은 프록시 객체이므로 항상 false
...
}
하지만, 위와 같은 방법은 통하지 않는다. 그 이유는 프록시 객체는 대상 객체가 대상 객체 자신을 리턴하는 경우 프록시 객체를 리턴하도록 만들어지기 때문이다.
- proxy.self() 메서드는 대상 객체의 self() 메서드를 호출한다.
- 대상 객체의 self() 메서드는 자기 자신을 리턴한다.
- proxy.self() 메서드는 대상 객체가 리턴한 객체가 대상 객체와 동일한지 확인한다.
- 동일하다면 프록시 객체 자신을 리턴한다.
- A 객체의 a() 메서드는 B 인터페이스를 파라미터로 갖는다.
- B 인터페이스의 b() 메서드는 A를 파라미터로 갖는다.
- A 객체의 a() 메서드는 파라미터로 전달받은 B 인스턴스의 b() 메서드를 호출할 때 자기 자신을 전달한다.
Order o = getOrder(xxx);
Content proxy = o.getContent(); // Content는 프록시