3

根据单例设计模式,在整个“应用程序”中只能存在一个单例类的实例。但是,此定义假定 Singleton 类本身由单个类加载器加载一次。但是,在理论上的多 VM 环境中,同一个 Singleton 类有可能被多个类加载器加载。

开发人员如何确保在整个多 VM 环境中只有一个实例可用?有没有办法确保类加载仅通过单个类加载器发生?

4

3 回答 3

4

简短的回答?在非托管环境中,您不能。这就是单例模式的谬误,在正常环境下确实是不可能执行的。想一想:类对象存在于类加载器的上下文中,可以有多个对象存在于同一个 VM 中。并且可以有多个虚拟机存在于同一个节点内。多个节点可以是同一个应用程序的一部分。当然,如果这些类对象中的每一个被实现为单例,它们都会创建该类的单个实例,但是可以(可能)有许多类对象。

正如 Oleg Mikheev 的回答中提到的,Java EE 规范的最新版本提供了单例会话 bean 的规定;我不熟悉它们的实现细节,但我想知道它们是否真的,真的是单例:存在于集群应用程序中的单个实例——或者它们是否只是一个逻辑抽象,多个实例存在于不同的应用程序中节点。

于 2013-01-28T22:47:08.810 回答
0

尝试摆脱单例,这是面向对象编程中的反模式。相反,使您的对象可组合并在构造函数中传递依赖关系。这是您解决问题根本原因的方法,并且将始终确保您只有一个类的实例。

于 2013-01-29T15:23:02.617 回答
0

好吧,我假设你在谈论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 类没有任何模式代码。我希望我有帮助。

于 2013-01-28T22:55:14.233 回答