【问题标题】:How can I make use of a Singleton in a common library, in multiple modules, without them conflicting?如何在公共库中的多个模块中使用 Singleton 而不会发生冲突?
【发布时间】:2014-03-06 12:22:55
【问题描述】:

我有一个在公共库中实现一些基本功能的单例对象。它比这复杂得多,但为了记录问题,下面是单例实现,除了一个内部属性/对象之外,所有内部属性/对象都被删除:

package mystuff.lib.common

public class CommonSingleton {


    public final static CommonSingleton INSTANCE = new CommonSingleton();

    private Cache<String, Object> cache = null;

    private CommonSingleton() {
        // Exists only to defeat instantiation.
        Initialize();
    }

    private void Initialize() {
        cache = null;
    }

    public Cache<String, Object> getCache() {
        if (cache == null) {
            cache = new Cache<String, Object>();
        }
        return cache;
    }

    public void Reload() {
        Initialize();
    }

}

我已将该库包含在两个单独的项目中。该库未安装在运行代码的机器上。在 IntelliJ IDEA 中创建工件时,公共库的内容包含在每个 JAR 中。

两个项目(JAR 文件)在父应用程序中加载和运行(作为插件),因此它们都在同一个 JVM 中加载。我希望每个 JAR 都有自己的这个公共单例的副本,因为它们毕竟是单独的 JAR 文件。它只包含在一个公共库中,因为我的许多项目都需要相同的功能,我宁愿将这个库包含在我的项目中,也不愿让代码的多个副本随处可见。

项目/模块 #1:TestPluginOne.JAR

package mystuff.plugins.TestPluginOne;

import mystuff.lib.common.CommonSingleton;
import mystuff.lib.common.Objects.Cache;

public class TestClassOne {

    public TestClassOne() {
    }

    public Boolean SaveItemInCache(String key, String content) {
        return CommonSingleton.INSTANCE.getCache().add(key, content, 30);
    }

    public void Reload() {
        CommonSingleton.INSTANCE.Reload();
    }

}

项目/模块#2:TestPluginTwo.JAR

package mystuff.plugins.TestPluginTwo;

import mystuff.lib.common.CommonSingleton;
import mystuff.lib.common.Objects.Cache;

public class TestClassTwo {

    public TestClassTwo() {
    }

    public String GetItemFromCache(String key) {
        return CommonSingleton.INSTANCE.getCache().get(key);
    }

    public void Reload() {
        CommonSingleton.INSTANCE.Reload();
    }

}

然而,我发现当我在两个模块中使用这个对象时(请注意,单独的 JAR 文件),它们都在访问同一个单例。虽然这个带有 Cache 对象的特定示例可能不会表明这有任何问题,但我需要将它们视为单独的对象有很多原因。有许多内部/私有成员变量需要包含不同的值,具体取决于使用此 Singleton 的项目/模块/插件。

我很困惑。当我在 .NET 中(例如)在单​​独的 DLL 中执行此操作时,每个 DLL 都维护它自己的该公共对象的内部实例。

我怎样才能将这个通用的 Singleton 功能包含在来自通用库的单独项目中,而不共享 Singleton 对象的实例?

谢谢!

【问题讨论】:

  • 只是想知道,ThreadLocal 可以在这里工作吗? ThreadLocal&lt;Singleton&gt;?
  • @vikingsteve 我以前没有使用过 ThreadLocal。我对此进行了调查,但我认为它并不真正适用于我的情况。此外,我相信我们的插件运行的宿主应用程序无论如何都是单线程的。
  • 当我只有一个插件启动时,架构可以完美运行。第一次访问公用功能时,会传入对父插件对象的引用,以便公用库能够正常运行。我遇到的问题是,当涉及第二个模块(JAR)时,实际的公共对象是共享的,而不仅仅是实现。所以第二个插件传入了对自身的引用,覆盖了第一个插件的初始化。
  • 听起来你只需要硬着头皮使用两个不同的单例。
  • 是的。但这比这要复杂得多。公共库中定义的所有对象都使用公共单例及其所有对象。当插件启动时,我将插件的一个实例传递给公共单例,然后从任何公共库函数中引用该实例。现在我发现每个插件之间的公共单例实例之间没有隔离,我将不得不将插件对象的实例传递给几乎每个公共库函数和/或对象实例化。你能说BARF吗!?!

标签: java jar intellij-idea singleton


【解决方案1】:

/* the evil of singletons 的一个非常纯粹的例子。 */

恐怕只要同一个类加载器试图加载同一个类,这个类就只会被加载一次。无论涉及哪些 jar:jar 与 DLL 不同,它只是文件系统的签名扩展。

我能想象的最简单的(关于所需的代码更改)是在每个插件中对您的单例进行子类化,以便实例化一个单独的类,并具有单独的状态。

否则,您可以显式初始化实例或使用依赖注入来为您提供实例。

【讨论】:

  • 是的。我同意如果不正确使用单例可能是邪恶的,但是当我需要确保只存在一个对象的实例(如日志或缓存机制)时,真的没有更好的方法了。我最初没有将单例子类化的原因是因为单例没有默认构造函数,所以我不能在子类中添加自己的私有构造函数来初始化对象。
  • 解决方案是使类抽象并声明一个所有子类必须实现的抽象方法来初始化自己,然后从抽象基类的私有构造函数中调用该方法。但是,这也不起作用,因为公共静态成员变量 INSTANCE 无法在抽象类中初始化。
  • 我唯一的选择是简单地手动继承单例,然后手动重写 Initialize() 方法并确保调用 super.Initialize()。如果其他人决定在我们的另一个项目中实施相同的解决方案,这将是混乱的、手动的,并且不会强制执行或自动处理。
  • 从头开始。无论如何,我不能对没有默认构造函数的类进行子类化。当然希望 Java 有一个类似于 DLL 的构造,所以我不必将所有这些代码复制到我使用它的每个项目中。呃……
  • 有没有其他方法可以在 Java 中创建一个通用的函数库来提供使用之间的隔离?我无法创建通用功能并从多个插件中重用它,这似乎很可笑。几乎所有常用功能都会记录错误和/或将信息发送到需要引用插件名称的控制台,以便操作员知道消息来自何处。我不想将插件的实例传递到公共库中的每个单独的方法调用或对象创建中。
猜你喜欢
  • 2015-04-11
  • 2021-06-20
  • 1970-01-01
  • 1970-01-01
  • 2010-12-28
  • 1970-01-01
  • 2011-10-24
  • 2021-01-13
  • 1970-01-01
相关资源
最近更新 更多