Effective Java ‐ Item 90⚠️ - dnwls16071/Backend_Summary GitHub Wiki
아이템 90 - 직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라.
- 여태까지 공부한 내용을 토대로 봤을 때,
Serializable인터페이스를 구현하기로 결정한 순간 언어의 정상 메커니즘인 생성자 이외의 방법으로 인스턴스를 생성할 수 있게 된다. - 결국 버그와 보안 문제로 이어질 가능성이 매우 커진다.
- 이런 위험을 크게 줄여줄 기법으로 바로 직렬화 프록시 패턴(Serialization Proxy Pattern)이 있다.
public final class Period implements Serializable {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
if (this.start.compareTo(this.end) > 0) {
throw new IllegalArgumentException(start + "가 " + end + "보다 늦다.");
}
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
}
private Object writeReplace() {
return new SerializationProxy(this);
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("프록시가 필요합니다");
}
//바깥 클래스의 논리적 상태를 정밀하게 표현하는 중첩 클래스(Period의 직렬화 프록시)
private static class SerializationProxy implements Serializable {
private static final long serialVersionUID = 234098243823485285L;
private final Date start;
private final Date end;
SerializationProxy(Period p) {
this.start = p.start;
this.end = p.end;
}
private Object readResolve() {
return new Period(start, end);
}
}
}
- 가짜 바이트 스트림 공격과 내부 필드 탈취 공격을 프록시 수준에서 차단해준다.
- 필드들을
final로 선언해도 되므로Period클래스를 진정한 불변으로 만들 수 있다. - 역직렬화때 유효성 검사를 하지 않아도 된다.
- 역직렬화한 인스턴스와 원래의 직렬화된 인스턴스의 클래스가 달라도 정상 작동한다.
직렬화 프록시 패턴의 한계
- 클라이언트가 멋대로 확장할 수 있는 클래스에는 적용할 수 없다.
- 객체그래프 순환이 있는 클래스에는 적용할 수 없다.
- 방어적 복사보다 느리다.
확장할 수 없는 클래스라면 가능한 직렬화 프록시 패턴을 사용하자.