[Effective Java] 직렬화된 인스턴스 대신 직렬화 프록시의 사용을 고려하자. |
-
Serializable 인터페이스를 구현할 때는 결함과 보안 문제가 생길 가능성이 커진다.
정상적인 생성자 대신 언어 영역 밖의 메커니즘을 사용해서 인스턴스가 생성되기 때문이다.
그런 위험을 현저히 줄이는 방법이 직렬화 프록시 패턴(Serialization proxy pattern) 이다.
-
직렬화 프록시 패턴은 직렬화 가능 클래스의 private static 중첩 클래스를 설계한다.
직렬화 프록시(serialization proxy) 라고 하는 inner 클래스는 외곽 클래스를 매개 변수 타입으로 하는 단일 생성자를 갖는다.
그리고 이 생성자는 자신의 인자로부터 데이터만 복사한다.
일관성 검사나 방어 복사도 할 필요가 없다.
그리고 나서 outer class 와 inner class 가 모두 implements Serializable 이여야 한다.
outer class 에 writeReplace 함수를 추가하여 해당 proxy 를 생성해준다.
private Object writeReplace(){
return new SerializationProxy( this );
}
writeReplace 메소드를 통해 외곽 클래스의 인스턴스 대신 SerializationProxy 인스턴스를 직렬화한다.
outer class 에 readObject 를 추가한다.
// 방어를 위함
private void readObject(ObjectInputStream stream) throws InvalidObjectException{
throw new InvalidObjectException("Proxy required");
}
마지막으로 proxy 에 readResolve 메소드를 추가한다.
private Object readResolve(){
return new OuterClass(param1, param2);
}
-
serialzation proxy pattern 은 언어 영역 밖의 특성을 배제한다.
모든 다른 인스턴스처럼 동일한 생성자, static 팩토리 메소드를 사용해서 역직렬화된 인스턴스를 생성하기 때문이다.
따라서 역직렬화된 인스턴스가 자신의 클래스가 갖는 불변 규칙을 지키는지 별도로 확인하지 않아도 된다.
-
직렬화 프록시 패턴은 바이트 스트림 위조 공격과 내부 필드 훔치기 공격을 즉각 퇴치시킨다.
또한 내부 필드들이 final 이 될 수도 있다.
-
직렬화 프록시 패턴에도 2가지 제약이 있다.
클라이언트가 서브 클래스를 만들 수 있는 클래스와는 호환이 안 된다.
또한 자신의 객체 그래프가 순환 관계를 갖는 클래스들도 호환이 안 된다.
즉, 직렬화 프록시의 readResolve 메소드 내부에서 그 객체의 메소드를 호출하려 하면 ClassCastException 예외가 발생할 것이다.
직렬화 프록시만 생겼을 뿐 아직은 그 객체가 생성되지 않았기 때문이다.
-
직렬화 프록시 패턴의 성능과 안전성은 공짜가 아니다.
방어 복사를 사용하는 경우보다 더 많은 시간과 자원을 사용한다.
Summary
클라이언트가 서브 클래스를 만들 수 없는 클래스에 readObject 나 writeObject 메소드를 작성해야 한다고 생각될 때는
언제든지 직렬화 프록시 패턴의 사용을 고려하자.
까다로운 불변 규칙을 갖는 객체를 직렬화하는 가장 쉬운 방법이 이 패턴일 것이다.
'프로그래밍 놀이터 > 디자인 패턴, 리펙토링' 카테고리의 다른 글
Visitor Pattern ( 방문자 패턴, visitor 패턴 ) (0) | 2017.06.29 |
---|---|
[도서 목차 정리] Effective Java (0) | 2017.03.30 |
[Effective Java] 인스턴스 제어에는 readResolve 메소드보다 enum 타입을 사용하자. (0) | 2017.03.27 |
[Effective Java] 방어 가능한 readObject 메소드를 작성하자 (0) | 2017.03.24 |
[Effective Java] 독자적인 직렬화 형태의 사용을 고려하자 (0) | 2017.03.23 |
댓글