Skip to content

[아이템 90] 직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라 #105

@jupyter471

Description

@jupyter471

📌 [아이템 90] 직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라

✨ 핵심 내용

직렬화 프록시 패턴을 통해 Serializable의 위험을 크게 줄일 수 있다

⭐  1. 바깥 클래스의 논리적 상태를 정밀하게 표현하는 중첩 클래스를 설계해 private static으로 선언한다

⇒ 이때 중첩 클래스가 바깥 클래스의 직렬화 프록시다

  • 중첩 클래스의 생성자는 단 하나여야하고 바깥 클래스를 매개변수로 받아야한다
    • 인스턴스의 데이터를 복사함
private static class Serializationproxy implements Serializable {
		private final Date start;
		private final Date end;
		
		**SerializationProxy**(Period p) {
				this.start = p.start;
				this.end = p.end;
		}
		
		private static final long serialVersionUID =
							234098243823485285L; // 아무 값이나 상관없다. (아이템87)
}
  1. 바깥 클래스에 writeReplace 메서드를 추가한다
//직렬화 프록시 패턴용 writeReplace 메서드
private Object writeReplace() {
		return new SerializationProxy(this);
}

→ 자바의 직렬화 시스템이 바깥 클래스의 인스턴스 대신 SerializationProxy의 인스턴스를 반환하게 하는 역할을 함

즉, 직렬화가 이뤄지기 전에 바깥 클래스 인스턴스를 직렬화 프록시로 변환해줌

//직렬화 프록시 패턴용 readObject 메서드
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
				throw new InvalidObjectException("프록시가 필요합니다.");
}

→ 해당 메서드를 바깥 클래스에 추가하면 불변식 훼손 공격을 막을 수 있다

  1. 바깥 클래스와 논리적으로 동일한 인스턴스를 반환하는 readResolve 메서드를 SerializationProxy 클래스에 추가한다

    → 역직렬화 시 직렬화 시스템이 직렬화 프록시를 다시 바깥 클래스의 인스턴스로 변환하게 해준다

직렬화는 생성자를 이용하지 않고 인스턴스를 생성하는데, 해당 패턴은 이러한 상당 부분을 제거한다!

⇒ 일반 인스턴스를 만들 때와 똑같은 생성자, 정적 팩터리, 혹은 다른 메서드를 사용해 역직렬화된 인스턴스를 생성한다

→ 역직렬화된 인스턴스가 해당 클래스의 불변식을 만족하는지 검사할 다른 수단 필요 X

  • 직렬화 프록시는 필드를 final로 선언해도되므로 클래스를 불변으로 만들 수 있다
  • 직렬화 프록시 패턴은 역직렬화한 인스턴스와 원래의 직렬화된 인스턴스의 클래스가 달라도 정상 작동한다!
  • ex) EnumSet

직렬화 프록시 패턴의 한계

  1. 클라이언트가 멋대로 확장할 수 있는 클래스(아이템 19)에는 적용할 수 없다
  2. 객체 그래프에 순환이 있는 클래스에 적용할 수 없다
  3. 대가가 따른다! 느리다!

💡 새롭게 알게 된 점

  • 학습하면서 이전과 다르게 이해한 점이나 새롭게 알게 된 개념을 공유해주세요.

📚 정리

제3자가 확장할 수 없는 클래스라면 가능한 한 직렬화 프록시 패턴을 사용!


📢 댓글로 각자의 학습 내용을 공유해주세요!

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions