我有一个看起来像这样的类:
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 连接进来,然后不管它,它不会是空的,因为你已经连接了它(如果是,你的程序初始化就会失败)。
是的,如果你想要一些全球性的东西,单例范围是正确的选择。这里值得一提的几件事是:
- Spring 中的默认范围是单例,因此您不需要显式将 Bean 设置为单例作用域。
- 使用 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。