Item 90. Serialization Proxy Pattern

μ§λ ¬ν™”λœ μΈμŠ€ν„΄μŠ€ λŒ€μ‹  직렬화 ν”„λ‘μ‹œ μ‚¬μš©μ„ κ²€ν† ν•˜λΌ

Serializable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” μˆœκ°„ 정상적인 μƒμ„±μž μ΄μ™Έμ˜ λ°©λ²•μœΌλ‘œ μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 수 있게 λ˜λ©΄μ„œ, λ²„κ·Έλ‚˜ λ³΄μ•ˆ λ¬Έμ œμ— λ…ΈμΆœλ˜κ²Œ λœλ‹€. 이런 μœ„ν—˜μ„ 크게 μ€„μ—¬μ£ΌλŠ” κΈ°λ²•μœΌλ‘œ 직렬화 ν”„λ‘μ‹œ νŒ¨ν„΄μ΄ μžˆλ‹€.

κ΅¬ν˜„ 방법

직렬화 ν”„λ‘μ‹œ νŒ¨ν„΄μ˜ κ΅¬ν˜„ 방법은 λ‹€μŒκ³Ό κ°™λ‹€.

  • λ°”κΉ₯ 클래슀의 논리적 μƒνƒœλ₯Ό ν‘œν˜„ν•˜λŠ” 쀑첩 클래슀λ₯Ό 섀계해 private static으둜 μ„ μ–Έ

  • 쀑첩 클래슀의 μƒμ„±μžλŠ” 단 ν•˜λ‚˜λ‘œ μ œν•œν•˜λ©°, λ°”κΉ₯ 클래슀λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ λ°›μ•„ λ‹¨μˆœνžˆ λ„˜μ–΄μ˜¨ μΈμŠ€ν„΄μŠ€ 데이터λ₯Ό 볡사

  • λ°”κΉ₯ ν΄λž˜μŠ€μ™€ 직렬화 ν”„λ‘μ‹œ λͺ¨λ‘ Serializable을 κ΅¬ν˜„


class Period implements Serializable {

    // final ν‚€μ›Œλ“œ μ‚¬μš©
    private final Date start;
    private final Date end;

    public Period(Date start, Date end) {
        this.start = start;
        this.end = end;
    }

    // 직렬화할 객체λ₯Ό λŒ€μ²΄ν•  λ‹€λ₯Έ 객체λ₯Ό λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œ
    private Object writeReplace() {
        return new SerializationProxy(this);
    }

    // 역직렬화 κ³Όμ •μ—μ„œ 호좜
    private void readObject(ObjectInputStream stream) throws InvalidObjectException {
        throw new InvalidObjectException("Proxy required");
    }

    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);
        }
    }

μœ„μ²˜λŸΌ κ΅¬ν˜„λœ 각각의 λ©”μ„œλ“œλ“€μ€ μ•„λž˜μ™€ 같은 역할을 μˆ˜ν–‰ν•˜λ©΄μ„œ λ³΄μ•ˆ 문제λ₯Ό ν•΄κ²°ν•˜κ²Œ λœλ‹€.

  • writeReplace: λ°”κΉ₯ 클래슀 μΈμŠ€ν„΄μŠ€ λŒ€μ‹  직렬화 ν”„λ‘μ‹œ 객체λ₯Ό λ°˜ν™˜ν•˜μ—¬ 직렬화 쀑에 μƒμ„±μž ν˜ΈμΆœμ„ λ§‰μŒ

  • readObject: 역직렬화가 μ§μ ‘μ μœΌλ‘œ 이루어지지 μ•Šλ„λ‘ μ˜ˆμ™Έλ₯Ό 던져 ν”„λ‘μ‹œ 객체λ₯Ό ν†΅ν•΄μ„œλ§Œ 역직렬화가 이루어지도둝 함

  • readResolve: μ—­μ§λ ¬ν™”λœ ν”„λ‘μ‹œ 객체λ₯Ό λ°”κΉ₯ 클래슀 μΈμŠ€ν„΄μŠ€λ‘œ λŒ€μ²΄ν•˜μ—¬ λ°˜ν™˜

μž₯점

직렬화 ν”„λ‘μ‹œ νŒ¨ν„΄μ„ μ‚¬μš©ν•˜λ©΄ μ•„λž˜μ™€ 같은 μž₯점을 얻을 수 μžˆλ‹€.

  1. μ§„μ •ν•œ λΆˆλ³€ 클래슀: 멀버 ν•„λ“œ final μ„ μ–Έν•˜μ—¬ λΆˆλ³€μ„± 보μž₯ κ°€λŠ₯

  2. 역직렬화 μœ νš¨μ„± 검사 λΆˆν•„μš”: 직렬화 λŒ€μƒμ΄ μ§μ ‘μ μœΌλ‘œ λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 역직렬화 κ³Όμ •μ—μ„œ μœ νš¨μ„± 검사λ₯Ό ν•  ν•„μš”κ°€ μ—†μŒ

  3. 역직렬화 μΈμŠ€ν„΄μŠ€ != μ›λž˜μ˜ μΈμŠ€ν„΄μŠ€: μ—­μ§λ ¬ν™”λœ μΈμŠ€ν„΄μŠ€κ°€ μ›λž˜μ˜ μΈμŠ€ν„΄μŠ€μ™€ λ‹€λ₯΄κ²Œ λ˜μ–΄λ„ λ¬Έμ œκ°€ μ—†μŒ

이 쀑 3λ²ˆμ— λŒ€ν•œ μž₯점은 EnumSet 클래슀λ₯Ό μ‚¬μš©ν•œ μ˜ˆμ‹œλ₯Ό 톡해 확인할 수 μžˆλ‹€.

EnumSet 클래슀의 직렬화 ν”„λ‘μ‹œ νŒ¨ν„΄

EnumSet 클래슀 생성 μ‹œ RegularEnumSetκ³Ό JumboEnumSet 두 κ°€μ§€ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•˜κ²Œ λ˜λŠ”λ°, 이 두 μΈμŠ€ν„΄μŠ€μ˜ μ°¨μ΄λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  • RegularEnumSet μΈμŠ€ν„΄μŠ€: 64개 μ΄ν•˜μ˜ μ›μ†Œλ₯Ό κ°€μ§ˆ λ•Œ μ‚¬μš©

  • JumboEnumSet μΈμŠ€ν„΄μŠ€: 64개 μ΄μƒμ˜ μ›μ†Œλ₯Ό κ°€μ§ˆ λ•Œ μ‚¬μš©

64개짜리 μ—΄κ±° νƒ€μž…μ„ κ°€μ§„ RegularEnumSet μΈμŠ€ν„΄μŠ€λ₯Ό μ§λ ¬ν™”ν•˜κ³  μ›μ†Œλ₯Ό μΆ”κ°€ν•œ ν›„ μ—­μ§λ ¬ν™”ν•˜λ©΄ JumboEnumSet μΈμŠ€ν„΄μŠ€κ°€ μƒμ„±λ˜μ–΄μ•Ό ν•˜λŠ”λ°, 이λ₯Ό μœ„ν•΄ EnumSet ν΄λž˜μŠ€λŠ” 직렬화 ν”„λ‘μ‹œ νŒ¨ν„΄μ„ μ‚¬μš©ν•΄ μ μ ˆν•œ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜λ„λ‘ κ΅¬ν˜„λ˜μ–΄ μžˆλ‹€.

public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
        implements Cloneable, Serializable {

    // ...

    // 
    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<?>[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");

        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }

    private static class SerializationProxy<E extends Enum<E>> implements Serializable {

        private static final long serialVersionUID = 362491234563181265L;
        // 이 EnumSet의 μ›μ†Œ νƒ€μž…
        private final Class<E> elementType;
        // 이 EnumSet의 μ›μ†Œλ“€
        private final Enum[] elements;

        SerializationProxy(EnumSet<E> set) {
            elementType = set.elementType;
            elements = set.toArray(ZERO_LENGTH_ENUM_ARRAY);
        }

        private Object readResolve() {
            EnumSet<E> result = EnumSet.noneOf(elementType);
            for (Enum e : elements) {
                result.add((E) e);
            }
            return result;
        }
    }
}

Last updated

Was this helpful?