如何在java中创建一个真正的单例?
在多个类加载器中使用时,我遇到了我的单例问题。 例如,Singleton由多个EJB访问。 有没有办法创建一个在所有类加载器中只有一个实例的单例?
我正在寻找使用自定义类加载器或其他方式的纯java解决方案。
唯一的方法是让你的单例类由一个类加载器加载 – 例如,将该jar文件放在bootclasspath中。
静态变量本质上与类加载器相关联,类加载器加载包含该变量的类。 这就是它的工作方式。 如果您只需要一个实例,则需要该类仅由一个类加载器加载。
JavaEE应用服务器通常通过将单例设置为“服务”来解决此问题,其确切的定义和配置取决于所讨论的应用程序服务器。
例如,在JBoss中,您可以使用xyz-service.xml描述符来设置挂起JNDI或JMX树的单个对象,并且您的应用程序组件(例如您的EJB)将从树中获取单例。 这在一定程度上保护了您的基础类加载器语义。
J2EE在设计时考虑了集群,因此它支持的任何设计都必须与多个JVM一起使用。 我从你的问题中得知你不关心集群环境,所以在你的应用服务器上简单地插入JNDI应该这样做。 在Glassfish中,这被称为生命周期监听器 。 在启动之后,将您的单例插入JNDI,然后让其他所有内容进行JNDI查找以找到它。
请注意,GlassFish仍然可能会让您陷入困境,因为它可以将类序列化为JNDI,从而导致您获得不同的实例。 我怀疑它在一个JVM中实际上是这样做的,但是在你尝试之前你不会知道。
真正的底线答案是J2EE对全球真正的单身人士怀有敌意,围绕这个问题的J2EE方法是重新思考解决方案。 类似于保存值的数据库或其他一些外部服务可以确保只存在一个数据实例(即使表示数据的多个对象实例)是J2EE方式。
要获得TRUE Singleton
,您必须遵循以下指南:
- 让你的class级成为
final
。 其他人不能对它进行子类化并再创建一个实例 - 使您的Singleton实例成为
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问题:
在Java中实现单例模式的有效方法是什么?