跨多个类加载器使用时,我的单例存在问题。例如,由多个 EJB 访问的单例。有没有办法创建一个在所有类加载器中只有一个实例的单例?
我正在寻找使用自定义类加载器或其他方式的纯 Java 解决方案。
唯一的方法是让您的单例类由单个类加载器加载 - 例如,将该 jar 文件放在引导类路径中。
静态变量本质上与加载包含该变量的类的类加载器相关联。这就是它的工作方式。如果您绝对需要一个实例,则需要该类仅由一个类加载器加载。
JavaEE 应用服务器通常通过将单例设置为“服务”来解决此问题,其确切定义和配置取决于相关应用服务器。
例如,在 JBoss 中,您可以使用 xyz-service.xml 描述符来设置挂起 JNDI 或 JMX 树的单例对象,并且您的应用程序组件(例如您的 EJB)将从树中获取单例。这在某种程度上保护您免受底层类加载器语义的影响。
J2EE 在设计时考虑到了集群,因此它支持的任何设计都必须与多个 JVM 一起工作。我从您的问题中得知您不关心集群环境,因此只需简单地插入应用服务器上的 JNDI 即可。在 Glassfish 中,这称为生命周期侦听器。甚至在启动之后,将你的单例插入到 JNDI 中,然后让其他一切都执行 JNDI 查找以找到它。
请注意,GlassFish 在这里仍然可能会搞砸您,因为它可能会将类序列化为 JNDI,从而导致您获得不同的实例。我怀疑它实际上是在一个 JVM 中做到这一点的,但在你尝试之前你不会知道。
真正的底线答案是 J2EE 对全球真正的单例怀有敌意,而 J2EE 解决问题的方法是重新考虑解决方案。诸如保存值的数据库或可以确保仅存在一个数据实例(即使表示数据的对象的多个实例)存在的某些其他外部服务是 J2EE 方式。
要实现 TRUE Singleton
,您必须遵循以下准则:
final
. 其他人不能对它进行子类化并再创建一个实例private static final
private constructor
和public getInstance()
方法。Singleton
类是由one ClassLoader only
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 问题: