4

我正在设计下面的单例类,我知道我的单例可能会被破坏

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

下面的 url 已经建议了可以用其他方式破解单例的地方,但我的疑问是,正如这个 url 中所建议的那样,单例可以被类加载器破坏,同样的类也可以由两个不同的类加载器加载,因此,你可以只需在由两个不同类加载器加载的类中调用其 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);

请告知应采取什么措施来避免这种情况。

4

4 回答 4

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

类似的问题:

于 2013-03-10T12:18:13.060 回答
1

这是您担心的情况:

  1. 类加载器在其类路径上A没有。Singleton
  2. 类加载器B是其类路径的子类加载器。ASingleton
  3. 类加载器C是其类路径的子类加载器。ASingleton
  4. 两者BC加载Singleton.

根据 JLS ,当这种情况发生时,您最终会得到两种不同的类型,称为Singleton...。因此,当您创建各自的实例时,它们将是不同类型的实例。(可以说,“一个类型”不变量并没有被打破,因为类型不一样。但是,如果单例的目的是保持必须只保持一次的状态,那么技术论点就无关紧要了。 )

实际上,如果您确实遇到了这种情况,那么加载的另一个类B将无法使用...A的实例,反之亦然...因为类型不同。Effectively和的类集只会“知道”其中一个单例实例......分别。SingletonBC

无论如何,如果你确实需要B's 和C's 类来共享一个 的实例Singleton,实现它的方法是放在Singleton一个公共父/祖先类加载器的类路径上;例如A在这种情况下。


请告知应采取什么措施来避免这种情况。

在重新阅读本文时,您似乎正在寻找一种方法来阻止其他人编写使用类加载器意外或故意破坏不变量的代码。

我认为没有……除非您将其他人的代码视为不受信任并在沙箱中运行它,以防止他们创建类加载器,使用破坏抽象的反射等等。

于 2013-03-10T12:43:43.623 回答
0

我不认为这应该是一个问题,因为不同加载器加载的类是不兼容的,你将无法将两个实例都转换为SingletonObject.

于 2013-03-10T12:19:33.933 回答
0

为了避免这种情况,Singleton 的类应该由最顶层的类加载器加载。在这种复杂的情况下,这应该在应用程序启动时完成,在任何其他类加载器出现之前。首先调用getSingletonObject()方法来加载类并实例化单例。tern 中的子类加载器(例如自制)不应破坏类加载策略:首先在父类加载器中查找类,然后才尝试加载自身。如果中断,可能会出现多个 Singleton。

于 2013-03-10T12:54:42.883 回答