弹簧注入静态(全局)单例



我有一个看起来像这样的类:

public class Configurator {
    private static Configurator INSTANCE = null;
    private int maxRange = 1;
    // many other properties; each property has a default value
    private static synchronized Configurator getInstance() {
        if(INSTANCE == null)
            return new Configurator();
        return INSTANCE;
    }
    public static int getMaxRange() {
        getInstance().maxRange;
    }
    public static void setMaxRange(int range) {
        getInstance().maxRange = range;
    }
    // Getters and setters for all properties follow this pattern
}

它充当可以在应用程序启动时设置的全局配置对象,然后由整个项目中的数十个类使用:

// Called at app startup to configure everything
public class AppRunner {
    Configurator.setMaxRange(30);
}
// Example of Configurator being used by another class
public class WidgetFactory {
    public void doSomething() {
        if(Configurator.getMaxRange() < 50)
            // do A
        else
            // do B
    }
}

我现在正在将此代码导入到Spring项目中,并尝试配置我的Sprinig XML(bean)。我的猜测是,我可以像这样定义一个孤独Configurator豆(或类似的东西):

<bean id="configurator" class="com.me.myapp.Configurator" scope="singleton">
    <property name="maxRange" value="30"/>
    <!-- etc., for all properties -->
</bean>

这样,当WidgetFactory#doSomething执行时,Spring 已经加载了 Configurator 类并提前对其进行了配置。

我设置scope="singleton"是否正确,还是这无关紧要?我是否正确设置了静态属性?我还需要在这里做或考虑什么吗?提前谢谢。

作为设计模式的单例和 Spring 的单例设施之间存在一些差异。作为设计模式的单例将确保您为每个类装入器定义一个类对象。相比之下,Spring 的单例工具(和方法)将为每个 Spring 上下文定义一个实例。

在这种情况下,你可以利用 Spring 使用的getInstance()方法来抓取你的对象实例:

<bean id="configurator" class="com.me.myapp.Configurator" factory-method="getInstance">
</bean>

使用 Spring 时,singleton bean 作用域是默认的,因此您不需要定义它。

如果你想用configurator作为春豆,你必须把它注入到其他物体中,而不是用getInstance()来抓取它。所以在其他 Spring bean 中使用 @Autowired 或通过 xml 文件定义对 bean 的引用。如果你不重新组织configurator在其他类中的使用,就不会有区别,Spring 会实例化你的类,但你会像以前一样使用它。

我还看到您在设计单例时存在错误。getInstance()方法应该是公共的,其他方法不应是静态的。在您使用的示例中,您应该像这样使用单例:

Configurator.getInstance().someMethod()

在这种情况下,您实际上只使用 Singleton 类,而不实例化任何对象!请参阅关于 Singleton 的维基百科(带有 Java 示例),了解有关 Singleton 设计模式以及如何使用它的更多信息。


注意:值得了解并尝试将Configurator用作单例并使用 Spring 的单例功能。如果你这样做,好处将是你可以
  • 删除getInstance()方法
  • 公开构造函数
  • 让 Spring 实例化该单个对象。
默认情况下,

Bean 是单例的。 您可以通过春季网站找到此/更多信息。

您不应该在 getInstance 中实例化新的配置器,因为它不会引用弹簧加载的 bean,这可能会导致一些严重的问题。 你可以把这个 bean 连接进来,然后不管它,它不会是空的,因为你已经连接了它(如果是,你的程序初始化就会失败)。

是的,如果你想要一些全球性的东西,单例范围是正确的选择。这里值得一提的几件事是:

  1. Spring 中的默认范围是单例,因此您不需要显式将 Bean 设置为单例作用域。
  2. 使用 Spring,您无需编写单例模式样式代码,例如私有实例和工厂方法。那是因为春天会保证每个 Spring 只有一个实例容器。甚至不是说,你的工厂方法是私有的。

顺便说一下:这不是线程安全的:

if(INSTANCE == null)
        return new Configurator();
    return INSTANCE;
}

而这将是:

private static Configurator INSTANCE = new Configurator();

(预先初始化)

private static volatile Singleton _instance = null;

(使用易失性关键字进行延迟初始化)

它与Java分配内存和创建实例的方式有关。这不是原子的,而是分两步完成的,可能会受到线程调度程序的干扰。

另请参阅 http://regrecall.blogspot.de/2012/05/java-singleton-pattern-thread-safe.html。

最新更新