단일 모드 의 몇 가지 장면 을 파괴 하 는 해결 방안 과 원리
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
*/
}
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
디자인 모델 의 공장 모델, 단일 모델자바 는 23 가지 디자인 모델 (프로 그래 밍 사상/프로 그래 밍 방식) 이 있 습 니 다. 공장 모드 하나의 공장 류 를 만들어 같은 인 터 페 이 스 를 실현 한 일부 종 류 를 인 스 턴 스 로 만 드 는 것...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.