根据单例设计模式,在整个“应用程序”中只能存在一个单例类的实例。但是,此定义假定 Singleton 类本身由单个类加载器加载一次。但是,在理论上的多 VM 环境中,同一个 Singleton 类有可能被多个类加载器加载。
开发人员如何确保在整个多 VM 环境中只有一个实例可用?有没有办法确保类加载仅通过单个类加载器发生?
根据单例设计模式,在整个“应用程序”中只能存在一个单例类的实例。但是,此定义假定 Singleton 类本身由单个类加载器加载一次。但是,在理论上的多 VM 环境中,同一个 Singleton 类有可能被多个类加载器加载。
开发人员如何确保在整个多 VM 环境中只有一个实例可用?有没有办法确保类加载仅通过单个类加载器发生?
简短的回答?在非托管环境中,您不能。这就是单例模式的谬误,在正常环境下确实是不可能执行的。想一想:类对象存在于类加载器的上下文中,可以有多个对象存在于同一个 VM 中。并且可以有多个虚拟机存在于同一个节点内。多个节点可以是同一个应用程序的一部分。当然,如果这些类对象中的每一个被实现为单例,它们都会创建该类的单个实例,但是可以(可能)有许多类对象。
正如 Oleg Mikheev 的回答中提到的,Java EE 规范的最新版本提供了单例会话 bean 的规定;我不熟悉它们的实现细节,但我想知道它们是否真的,真的是单例:存在于集群应用程序中的单个实例——或者它们是否只是一个逻辑抽象,多个实例存在于不同的应用程序中节点。
尝试摆脱单例,这是面向对象编程中的反模式。相反,使您的对象可组合并在构造函数中传递依赖关系。这是您解决问题根本原因的方法,并且将始终确保您只有一个类的实例。
好吧,我假设你在谈论java。Java 语言本身并没有提供定义单例模式 IMO 的好方法。这就是为什么当我想在我的代码中使用特定模式时,比如那个,我使用 AspectJ 扩展它。
使用 AspectJ,您实际上可以“捕获”对任何构造函数的每次调用,并确保您始终返回相同的对象,因此无论如何都有一个单例。
如果您需要,这里是 AspectJ 中的协议代码:
package singleton.aspectJ;
import java.util.Hashtable;
import java.util.Map;
import org.aspectj.lang.annotation.AdviceName;
/**
* Defines the general behavior of the Singleton design pattern.
*
* Each concrete sub-aspect of SingletonProtocol defines the Singleton
* property for one or more types.
*
* The sub-aspect defines two things: <ol>
*
* <li> what types are <i>Singleton</i> <br>
*
* <li> what classes can access the <i>Singleton</i>'s constructor (if any)
* despite its property
* </ol>
*
* for this implementation we choose to illustrate that it is not necessary
* to provide a factory method for accessing the <i>Singleton</i>
* instance (like <i>getSingleton()</i>). The regular
* constructor may be used instead.
*
* @author Jan Hannemann
* @author Gregor Kiczales
* @author Pedro Martins
* @version 1.1.1, 21/11/2011
*/
public abstract aspect SingletonProtocol {
/**
* stores the <i>Singleton</i> instances
*/
private Map<Class<Object>, Object> singletonsTable =
new Hashtable<Class<Object>, Object>();
/**
* Defines the <i>Singleton</i> role.
*/
protected interface SingletonInterface{}
/**
* Placeholder for exceptions to the <i>Singleton</i>'s constructor
* protection. For example, non-singleton subclasses may need to
* access the protected constructor of the <i>Singleton</i> normally.
*
* An alternative implementation would be to define an interface
* for singleton exceptions similar to the one above.
*/
protected pointcut protectionExclusions();
private pointcut singletonInstantiator() : call ((SingletonInterface+).new(..)) && !protectionExclusions();
/**
* Protects the <i>Singleton</i>'s constructor. Creates the unique
* instance on demand and returns it instead of a new object.
*
* @return the singleton instance
*/
@SuppressWarnings("unchecked")
@AdviceName("newCatcher")
Object around() : singletonInstantiator(){
Class<Object> singleton = thisJoinPoint.getSignature().getDeclaringType();
if(!singletonsTable.containsKey(singleton))
singletonsTable.put(singleton, proceed());
return singletonsTable.get(singleton);
}
}
这是一个例子:
package singleton.aspectJ;
public aspect SingletonInstance extends SingletonProtocol{
declare parents: God implements SingletonInterface;
protected pointcut protectionExclusions():
call((Canaanites+).new(..));
}
在这种情况下,God 是一个单例,因为它实现了 SingletonInterface。另一个类 Cannnites 是上帝的孩子,但作为例外添加,因此它不是单例。令人惊讶的是,这是上帝的代码,它是孩子:
public class God {
//Code specific to this class's objective
}
public class Caanites extends God {
//Code specific to this class's objective
}
如您所见,令人惊奇的是,God 和 Caanites 类没有任何模式代码。我希望我有帮助。