단일 모드 의 몇 가지 장면 을 파괴 하 는 해결 방안 과 원리

10539 단어 디자인 모드
직렬 화 파괴 단일 모델 원리 및 해결 방안
/**
 *     :   ,    ,                 
 */
public class HungrySingleton implements Serializable {
    private final static HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        HungrySingleton instance = HungrySingleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
        oos.writeObject(instance);

        File file = new File("singleton_file");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        HungrySingleton newInstance = (HungrySingleton) ois.readObject();
        System.out.println(instance);
        System.out.println(newInstance);
        System.out.println(instance == newInstance);

    }
}

/**
 *     
 * com.nrael.design.pattern.creational.singleton.HungrySingleton@135fbaa4
 * com.nrael.design.pattern.creational.singleton.HungrySingleton@568db2f2
 * false
 */

그 결과 서열 화 와 반 서열 화 를 거 쳐 얻 은 대상 은 두 개 로 단일 모델 의 취지 에 어 긋 나 는 것 으로 나 타 났 다.
소스 코드 를 추적 한 결과 단일 클래스 에서 다음 과 같은 방법 을 추가 하면 상기 문 제 를 해결 할 수 있 음 을 발견 했다.이 방법 은 이름과 반환 형식 이 아래 에 쓰 인 것 과 일치 하 는 것 이 좋 습 니 다.
/**
 *     :   ,    ,                 
 */
public class HungrySingleton implements Serializable {
    private final static HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }

    //            
    private Object readResolve(){
        return hungrySingleton;
    }
}

테스트 실행 결 과 를 다시 호출 합 니 다.
com.nrael.design.pattern.creational.singleton.HungrySingleton@135fbaa4
com.nrael.design.pattern.creational.singleton.HungrySingleton@135fbaa4
true

원본 코드 추적:
// ObjectInputStream#readObject
public final Object readObject()
        throws IOException, ClassNotFoundException
    {
        if (enableOverride) {
            return readObjectOverride();
        }

        // if nested read, passHandle contains handle of enclosing object
        int outerHandle = passHandle;
        try {
            // TODO      
            Object obj = readObject0(false);
            handles.markDependency(outerHandle, passHandle);
            ClassNotFoundException ex = handles.lookupException(passHandle);
            if (ex != null) {
                throw ex;
            }
            if (depth == 0) {
                vlist.doCallbacks();
            }
            return obj;
        } finally {
            passHandle = outerHandle;
            if (closed && depth == 0) {
                clear();
            }
        }
    }

// ObjectInputStream#readObject0
private Object readObject0(boolean unshared) throws IOException {
    ...
    case TC_OBJECT:
         return checkResolve(readOrdinaryObject(unshared));
    ...
}

// ObjectInputStream#readOrdinaryObject
private Object readOrdinaryObject(boolean unshared){
    ...
    obj = desc.isInstantiable() ? desc.newInstance() : null;
    ... 
}

// ObjectInputStream#isInstantiable    true,   desc.newInstance(),           ,
//             HungrySingleton     readResolve            .
/**
 * Returns true if represented class is serializable/externalizable and can
 * be instantiated by the serialization runtime--i.e., if it is
 * externalizable and defines a public no-arg constructor, or if it is
 * non-externalizable and its first non-serializable superclass defines an
 * accessible no-arg constructor.  Otherwise, returns false.
 */
boolean isInstantiable() {
    requireInitialized();
    return (cons != null);
}

// ObjectInputStream#readOrdinaryObject
private Object readOrdinaryObject(boolean unshared){
    ...
    //   HungrySingleton    readResolve      desc.hasReadResolveMethod()        true
    if (obj != null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod())
        {
            //        HungrySingleton     readResolve     ,
            //          ,                obj,           
            Object rep = desc.invokeReadResolve(obj);
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {
                // Filter the replacement object
                if (rep != null) {
                    if (rep.getClass().isArray()) {
                        filterCheck(rep.getClass(), Array.getLength(rep));
                    } else {
                        filterCheck(rep.getClass(), -1);
                    }
                }
                handles.setObject(passHandle, obj = rep);
            }
        }

        return obj;
}

// 
readResolveMethod = getInheritableMethod(
                        //     readResolve       HungrySingleton         
                        cl, "readResolve", null, Object.class);

반사 공격 솔 루 션 및 원리 분석 
클래스 로 딩 할 때 단일 인 스 턴 스 대상 의 단일 모드 를 만 들 었 습 니 다:
/**
 *     :   ,    ,                 
 */
public class HungrySingleton implements Serializable {
    private final static HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

/**
 *            
 *     :        
 *     :                      ,                 
 */
public class Test {
    public static void main(String[] args) throws Exception {
        Class objectClass = HungrySingleton.class;
        Constructor constructor = objectClass.getDeclaredConstructor();
        HungrySingleton instance = HungrySingleton.getInstance();

        constructor.setAccessible(true);
        HungrySingleton object = (HungrySingleton) constructor.newInstance();

        System.out.println(instance);
        System.out.println(object);
        System.out.println(instance == object);

        /**
         *     
         * com.nrael.design.pattern.creational.singleton.v2.HungrySingleton@1540e19d
         * com.nrael.design.pattern.creational.singleton.v2.HungrySingleton@677327b6
         * false
         */
    }
}

 솔 루 션: 사유 화 구조 기 에 반사 호출 을 방지 하 는 코드 를 추가 합 니 다.
/**
 *     :   ,    ,                 
 */
public class HungrySingleton implements Serializable {
    private final static HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){
        //        ,                 
        if (hungrySingleton != null) {
            throw new RuntimeException("           ");
        }
    }

    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

========================================

/**
 *   :     ,               
 */
public class StaticInnerClassSingleton {
    //       
    private StaticInnerClassSingleton() {
        //                       ,                 
        if (InnerClass.staticInnerClassSingleton != null) {
            throw new RuntimeException("           ");
        }
    }

    //             ,                
    private static class InnerClass {
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }

    public static StaticInnerClassSingleton getInstance() {
        return InnerClass.staticInnerClassSingleton;
    }
}

Test 클래스 를 다시 호출 합 니 다. 콘 솔 출력 결 과 는 다음 과 같 습 니 다.
/**
 *     
 * Exception in thread "main" java.lang.reflect.InvocationTargetException
 * 	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 * 	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
 * 	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 * 	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
 * 	at com.nrael.design.pattern.creational.singleton.v2.Test.main(Test.java:16)
 * Caused by: java.lang.RuntimeException:            
 * 	at com.nrael.design.pattern.creational.singleton.v2.HungrySingleton.(HungrySingleton.java:14)
 * 	... 5 more
 */

클래스 로 딩 되 지 않 을 때 하나의 인 스 턴 스 대상 을 만 드 는 단일 모드 에 대해 서 는 어떻게 단일 사례 가 파괴 되 는 것 을 피 할 수 있 습 니까?
인증 결 과 는 피 할 수 없습니다. 클래스 로 딩 이 아 닐 때 하나의 대상 을 만 들 고 반사 적 인 방식 으로 모든 것 을 파괴 할 수 있 기 때 문 입 니 다.반사 기술 의 강 함 에 감탄 하 다.여 기 는 시연 하지 않 겠 습 니 다.
클론 파괴 사례
/**
 *     :   ,    ,                 
 *         
 */
public class HungrySingleton implements Serializable, Cloneable {
    private final static HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }

    //            
    private Object readResolve(){
        return hungrySingleton;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        // return super.clone();
        //     ,1.     Cloneable   ,2.    Cloneable   ,   clone   ,        getInstance   
        return getInstance();
    }
}

/**
 *                
 */
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        HungrySingleton hungrySingleton = HungrySingleton.getInstance();

        Method method = hungrySingleton.getClass().getDeclaredMethod("clone");
        method.setAccessible(true);
        HungrySingleton cloneHungrySingleton = (HungrySingleton) method.invoke(hungrySingleton);

        System.out.println(hungrySingleton);
        System.out.println(cloneHungrySingleton);
        System.out.println(hungrySingleton == cloneHungrySingleton);
        /**
         *     
         * com.nrael.design.pattern.creational.singleton.v4.HungrySingleton@7f31245a
         * com.nrael.design.pattern.creational.singleton.v4.HungrySingleton@6d6f6e28
         * false
         */

        /**
         *       ,        ,    
         *     
         * com.nrael.design.pattern.creational.singleton.v4.HungrySingleton@7f31245a
         * com.nrael.design.pattern.creational.singleton.v4.HungrySingleton@7f31245a
         * true
         */
    }
}

좋은 웹페이지 즐겨찾기