직렬화를 위해 해당 클래스 안에 만든 private static 중첩 클래스 (생성자는 1개만 있어야 함. 바깥 클래스를 매개변수로 받는)
이 때, 직렬화 대상 객체의 내부 상태를 저장하는 대신, 대리 객체의 상태를 저장
대리 객체는 직렬화된 데이터를 역직렬화할 때, 직렬화 대상 객체를 생성하고 초기화
class Period implements Serializable {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
this.start = start;
this.end = end;
}
private static class SerializationProxy implements Serializable {
private static final long serialVersionUID = 2123123123;
private final Date start;
private final Date end;
public SerializationProxy(Period p) {
this.start = p.start;
this.end = p.end;
}
/**
* Deserialize 할 때 호출된다.
* 오브젝트를 생성한다.
*/
private Object readResolve() {
return new Period(start, end);
}
}
/**
* 이로 인해 바깥 클래스의 직렬화된 인스턴스를 생성할 수 없다.
* 직렬화할 때 호출되는데, 프록시를 반환하게 하고 있다.
*
* Serialize할 때 호출된다.
*/
private Object writeReplace() {
return new SerializationProxy(this);
}
/**
* readObject, writeObject 가 있다면, 기본적으로 Serialization 과정에서
* ObjectInputStream, ObjectOutputStream이 호출하게 된다.
* 그 안에 커스텀 로직을 넣어도 된다는 것.
*/
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
// readObject는 deserialize할 때, 그러니까 오브젝트를 만들 때인데.
// 이렇게 해두면, 직접 Period로 역직렬화를 할 수 없는 것이다.
throw new InvalidObjectException("프록시가 필요해요.");
}
}
SerializationProxy클래스는Period` 객체를 인자로 받아서, 해당 객체의 시작일과 종료일을 저장
SerializationProxy 클래스는 readResolve() 메서드를 오버라이드하여, 역직렬화할 때 프록시 객체(SerializationProxy)를 실제 객체(Period)로 변환합니다. 이렇게 함으로써, 직렬화된 객체가 역직렬화될 때 Period 객체를 생성하도록 보장
또한, Period 클래스는 readObject() 메서드를 오버라이드하여, 역직렬화할 때 직접 Period 객체를 생성할 수 없도록 함
→ 직렬화된 객체를 역직렬화할 때, 프록시 객체(SerializationProxy)를 거쳐서 실제 객체(Period)를 생성하게 되므로, 객체의 불변성과 안전성을 보장할 수 있습니다.
2. 직렬화 프록시 패턴의 장점
멤버 필드를 final로 선언할 수 있기 때문에 진정한 불변으로 만들 수 있음
직렬화 프록시 패턴은 역직렬화한 인스턴스와 원래의 직렬화된 클래스가 달라도 정상적으로 동작
3. 직렬화 프록시 한계
클라이언트가 확장할 수 있는 클래스에는 적용 불가
확장 가능한 클래스는 하위 클래스가 생성될 때마다 이를 반영하여 새로운 직렬화 대상 클래스를 만들기 때문
객체 그래프 순환이 있는 클래스에는 적용 불가
직렬화 프록시는 객체 그래프에서 참조되는 모든 객체를 대상으로 직렬화를 수행하는데, 객체 그래프 순환이 있는 경우 무한루프에 빠질 가능성이 있기 때문
1. 직렬화 프록시 패턴이란?
SerializationProxy
클래스는
Period` 객체를 인자로 받아서, 해당 객체의 시작일과 종료일을 저장SerializationProxy
클래스는readResolve()
메서드를 오버라이드하여, 역직렬화할 때 프록시 객체(SerializationProxy
)를 실제 객체(Period
)로 변환합니다. 이렇게 함으로써, 직렬화된 객체가 역직렬화될 때Period
객체를 생성하도록 보장또한,
Period
클래스는readObject()
메서드를 오버라이드하여, 역직렬화할 때 직접Period
객체를 생성할 수 없도록 함→ 직렬화된 객체를 역직렬화할 때, 프록시 객체(
SerializationProxy
)를 거쳐서 실제 객체(Period
)를 생성하게 되므로, 객체의 불변성과 안전성을 보장할 수 있습니다.2. 직렬화 프록시 패턴의 장점
final
로 선언할 수 있기 때문에 진정한 불변으로 만들 수 있음3. 직렬화 프록시 한계
확장 가능한 클래스는 하위 클래스가 생성될 때마다 이를 반영하여 새로운 직렬화 대상 클래스를 만들기 때문
직렬화 프록시는 객체 그래프에서 참조되는 모든 객체를 대상으로 직렬화를 수행하는데, 객체 그래프 순환이 있는 경우 무한루프에 빠질 가능성이 있기 때문