我正在设计下面的单身人士课程,我知道我的单身人士可以被打破
public class SingletonObject {
private static SingletonObject ref;
private SingletonObject () //private constructor
{ }
public static synchronized SingletonObject getSingletonObject()
{
if (ref == null)
ref = new SingletonObject();
return ref;
}
public Object clone() throws CloneNotSupportedException
{throw new CloneNotSupportedException ();
}
}
以下网址已经建议单身可以被破坏cracking singleton with other ways,但我的查询是这个网址中建议单例可以被类加载器破坏,同样的类也可以由两个不同的类加载器加载,因此,您可以通过在由两个不同的类加载器加载的类中调用其getInstance()方法来创建单例类的两个实例。这种方法可以工作,而不必诉诸于违反私人构造函数。
ClassLoader cl1 = new URLClassLoader(new URL[]{"singleton.jar"}, null);
ClassLoader cl2 = new URLClassLoader(new URL[]{"singleton.jar"}, null);
Class<?> singClass1 = cl1.loadClass("hacking.Singleton");
Class<?> singClass2 = cl2.loadClass("hacking.Singleton");
//...
Method getInstance1 = singClass1.getDeclaredMethod("getInstance", ...);
Method getInstance2 = singClass2.getDeclaredMethod("getInstance", ...);
//...
Object singleton1 = getInstance1.invoke(null);
Object singleton2 = getInstance2.invoke(null);
请告知应采取什么措施来避免这种情况。
答案 0 :(得分:5)
如果你想创建真正的单例,你应该避免使用自定义类加载器 - 所有单例都应该由公共父类加载器加载。
解决您的问题的示例,链接如下:
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
答案 1 :(得分:1)
这是您担心的情况:
A
在其类路径上没有Singleton
。B
是A
的子类加载器,其类路径上有Singleton
。C
是A
的子类加载器,其类路径上有Singleton
。B
和C
加载Singleton
。当发生这种情况时,根据JLS,您最终会得到两个名为Singleton
的不同类型。因此,当您创建相应的实例时,它们将是不同类型的实例。 (可以说,“一种类型”不变量之一并没有被打破,因为类型不一样。但是,如果单身人士的目的是保持必须只持有一次的状态,那么技术论证就不合适了。 )
事实上,如果你最终遇到这种情况,那么B
加载的另一个类将无法使用A
的{{1}}实例...或反之亦然 ...因为类型不同。有效Singleton
和B
的类集只会分别“知道”其中一个单例实例。
无论如何,如果您确实需要C
和B
的类来共享C
的一个实例,那么实现它的方法就是放置Singleton
到公共父/祖先类加载器的类路径上;例如在这种情况下Singleton
。
请告知应采取什么措施来避免这种情况。
重新阅读本文时,您似乎正在寻找一种方法来阻止其他人编写使用类加载器意外或故意破坏不变量的代码。
我不认为有一个...除非你将其他人的代码视为不受信任,并在沙箱中运行,以防止他们创建类加载器,使用抽象破坏反射等等。
答案 2 :(得分:0)
我不认为这应该是一个问题,因为不同加载器加载的类是不兼容的,并且您将无法将两个实例都转换为SingletonObject
。
答案 3 :(得分:0)
为了避免这种情况,Singleton的类应该由最顶级的类加载器加载。在这种复杂的情况下,这应该在应用程序启动时完成,然后才会出现任何其他类加载器。首先调用getSingletonObject()
方法来加载类并实例化单例。 tern中的子类加载器(自制,例如)不应该破坏类加载策略:首先在父类加载器中查找类,然后尝试加载自身。如果休息,可以出现多个单身人士。