Spring注入一个静态(全局)单例

时间:2013-02-01 11:11:58

标签: java spring dependency-injection singleton

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

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:

<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"是否正确,或者这无关紧要?我正确设置静态属性吗?我还需要做什么或考虑一下吗?提前谢谢。

4 个答案:

答案 0 :(得分:11)

Singleton作为设计模式与Spring的单例设施有一些区别。 Singleton作为设计模式将确保每个Class Loader定义一个类对象。相比之下,Spring的单例工具(和方法)将为每个Spring Context定义一个实例。

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

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

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

如果要将configurator用作Spring bean,则必须将其注入其他对象,而不是使用getInstance()来获取它。所以在其他Spring bean中使用@Autowired或通过xml文件定义bean的引用。如果你没有重新组织其他类中configurator的使用,那就没有区别,Spring将实例化你的类,但你将像以前一样使用它。

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

Configurator.getInstance().someMethod()

在这种情况下,您实际上只使用Singleton类,而不实例化任何对象!有关Singleton设计模式以及如何使用它的更多信息,请参阅wikipedia on Singleton (with Java example)

<小时/> 注意:值得了解并尝试将Configurator用作Singleton并使用Spring的单例工具。如果你这样做,那么你可以

  • 删除getInstance()方法
  • 让您的构造函数公开
  • 让Spring实例化该单个对象。

答案 1 :(得分:1)

默认情况下,豆是单身。您可以通过spring网站找到这个/更多信息。

你不应该在getInstance中实例化一个新的Configurator,因为它不会引用spring加载的bean,这可能会导致一些严重的问题。您可以将此bean连接起来然后不管它,它不会为空,因为您已将其连接(如果您的程序初始化失败)。

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

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

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

    return INSTANCE;
}

这将是:

private static Configurator INSTANCE = new Configurator();

(急切初始化)

private static volatile Singleton _instance = null;

(使用volatile关键字进行延迟初始化)

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

另见http://regrecall.blogspot.de/2012/05/java-singleton-pattern-thread-safe.html