5

跨多个类加载器使用时,我的单例存在问题。例如,由多个 EJB 访问的单例。有没有办法创建一个在所有类加载器中只有一个实例的单例?

我正在寻找使用自定义类加载器或其他方式的纯 Java 解决方案。

4

4 回答 4

5

唯一的方法是让您的单例类由单个类加载器加载 - 例如,将该 jar 文件放在引导类路径中。

静态变量本质上与加载包含该变量的类的类加载器相关联。这就是它的工作方式。如果您绝对需要一个实例,则需要该类仅由一个类加载器加载。

于 2009-09-29T21:10:49.627 回答
5

JavaEE 应用服务器通常通过将单例设置为“服务”来解决此问题,其确切定义和配置取决于相关应用服务器。

例如,在 JBoss 中,您可以使用 xyz-service.xml 描述符来设置挂起 JNDI 或 JMX 树的单例对象,并且您的应用程序组件(例如您的 EJB)将从树中获取单例。这在某种程度上保护您免受底层类加载器语义的影响。

于 2009-09-29T21:14:30.807 回答
3

J2EE 在设计时考虑到了集群,因此它支持的任何设计都必须与多个 JVM 一起工作。我从您的问题中得知您不关心集群环境,因此只需简单地插入应用服务器上的 JNDI 即可。在 Glassfish 中,这称为生命周期侦听器。甚至在启动之后,将你的单例插入到 JNDI 中,然后让其他一切都执行 JNDI 查找以找到它。

请注意,GlassFish 在这里仍然可能会搞砸您,因为它可能会将类序列化为 JNDI,从而导致您获得不同的实例。我怀疑它实际上是在一个 JVM 中做到这一点的,但在你尝试之前你不会知道。

真正的底线答案是 J2EE 对全球真正的单例怀有敌意,而 J2EE 解决问题的方法是重新考虑解决方案。诸如保存值的数据库或可以确保仅存在一个数据实例(即使表示数据的对象的多个实例)存在的某些其他外部服务是 J2EE 方式。

于 2009-09-29T21:52:46.040 回答
0

要实现 TRUE Singleton,您必须遵循以下准则:

  1. 将您的课程设为final. 其他人不能对它进行子类化并再创建一个实例
  2. 将您的 Singleton 实例设为private static final
  3. 提供private constructorpublic getInstance()方法。
  4. 确保这个Singleton类是由one ClassLoader only
  5. 覆盖readResolve()方法并返回相同的实例,而不在反序列化过程中创建新实例。

示例代码:

final class  LazySingleton {
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder {
        private static final LazySingleton INSTANCE = new LazySingleton();
    }
    private Object readResolve()  {
        return LazyHolder.INSTANCE;
    }
}

有关详细信息,请参阅以下 SE 问题:

在 Java 中实现单例模式的有效方法是什么?

于 2016-06-11T13:55:45.980 回答