我正在将单例转换为Spring bean,因此,如果单例无法初始化,则整个Web应用程序的spring上下文将无法正确加载。

使Spring上下文无法正确加载的优点是,人们会在部署过程中注意到并修复配置。与使用"非春季bean"单例相反:当初始化期间引发异常时,没有人注意到..直到实际用户抱怨缺少功能。

我的更改正在按预期进行。.但是我不确定我是否做对了。

有什么想法吗?

代码如下:

public class MySingleton {
private static MySingleton INSTANCE = null;
private MySingleton(){}
public static MySingleton getInstance(){
if(INSTANCE == null){
synchronized(MySingleton.class){
if(INSTANCE == null){
try{
doWork()
}catch(Exception e){
throw new IllegalStateException("xyz", e);
}
INSTANCE = new MySingleton();
}
}
}
return INSTANCE;
}
private static void doWork() {
// do some work
}
}

在spring config xml中,bean将定义为:

class="com.MySingleton"

factory-method="getInstance" lazy-init="false" singleton="true">


编辑1:

使用此单例的类本身不是spring bean。它们只是非spring pojos,我无法转换为spring。他们必须依靠getInstance()方法来获得Singleton。

编辑2 :(将我在下面所做的评论复制到此描述部分中)

我试图针对两件事:

我希望Spring初始化单例。因此,如果

初始化失败,然后应用程序加载失败。

我希望其他类可以使用类而不必依赖contextAwareObj.getBean(" MySingleton")

编辑3(最终):

我决定让该类成为单身..而不是使它成为弹跳豆。如果初始化失败,它将在日志文件中记录一些内容。.希望进行部署的人员注意到这一点。...我放弃了前面提到的方法,因为我认为它将在将来造成维护方面的噩梦,因此我不得不在-单例-或-春豆之间选择。我选择单身人士。

我已经看到这种双重检查的方法不受欢迎。 尝试使用enum。

单例作用域是默认范围,但看起来不错。 我通常不会在春季应用程序中显式地实现单例模式(例如getInstance()),因为容器创建的bean默认情况下都是单例的。

@凯文 我无法摆脱getInstance()。 因为使用该Singleton的类,所以根本无法使用spring bean。 春季豆类,非春季豆类,随机类的静态方法的类将使用此Singleton。

@Kevin是正确的,因为默认情况下,bean是单例。 您的定义不正确。 它应该是scope="singleton",因为这是默认设置,但是可以通过将定义简化为来消除

您必须将INSTANCE字段声明为volatile,以便双重检查锁定才能正常工作。

请参阅有效的Java,项目71。

是的,我将添加。

也不能将synchronized块向外移动以包括is-null检查(并删除冗余检查)吗? (尽管我想如果您希望代码大量调用getInstance() ...)

@Darien就是这样。仔细检查锁定的目的是避免同步,除非实际需要同步。"如果您需要使用延迟初始化来提高实例字段的性能,请使用仔细检查的惯用法。这种惯用法避免了在初始化字段后访问字段时发生锁定的费用(项67)。"

有一种比使用" volatile"关键字更好的方法,该关键字在某些Java版本中不可用。请参阅:en.wikipedia.org/wiki/

一个更简单的解决方案是使用Guavas Memioze Supplier

"双重检查锁定被破坏"声明cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

为什么首先使用单例模式?只需让Spring为您创建bean(使用默认的singleton范围)并...使用它即可。当然,总是有人可以手工创建bean,但是就我而言,这从来都不是问题。

依赖注入和Spring管理的bean生命周期将极大地简化您的生活(只要看看可以避免多少陷阱即可)。还要注意,从c-tor或@PostContruct方法引发的异常将传播并导致应用程序上下文启动也失败。

更新:我明白你的意思。这就是我的想法:

@Service
public class Singleton {
private static AtomicReference INSTANCE = new AtomicReference();
public Singleton() {
final Singleton previous = INSTANCE.getAndSet(this);
if(previous != null)
throw new IllegalStateException("Second singleton" + this +" created after" + previous);
}
public static Singleton getInstance() {
return INSTANCE.get();
}
}

让Spring做好自己的工作。您可以尽可能使用DI,而必须使用Singleton.getInstance()。

还有更多的核心解决方案,例如编译时的AspectJ编织和将Spring bean基本上注入所有内容。

我这样做是为了避免执行以下操作:contextAwareObj.getBean("MySingleton");许多使用此MySingleton类的类,是arent spring bean ..并且我不能向其中注入任何spring bean。因此,无论我使用contextAwareObj.getBean("MySingleton");还是使用getInstance()方法..鉴于该选择,我更喜欢使用getInstance()方法。

谢谢,常见问题BTW。请看我的编辑。

您的更新似乎可以正常进行..但我不无论如何..我选择不制作弹簧豆...(请参阅上面的Edit3)。谢谢。

您对使用Spring管理的Singleton的建议很有意义。很少有我们不希望让Spring管理类似于Singleton的类的需求。另外,使用单例实例的AtomicReference的代码片段也非常有帮助。感谢分享!

我不确定为什么要这么做。当您告诉Spring Bean应该是单例时,相应的类不必是单例,也不需要工厂。 Spring只会创建一个实例。

链接的文章对我来说毫无意义,因为没有注入发生,因此我可以看到:" AnyService"正在调用单例工厂方法;在应用上下文中引用单例之前,它是无关紧要的,而且似乎没有其他bean引用它。

我正在将spring bean做成单例,因此应用程序中的其他类不必这样做:contextAwareObj.getBean("MySingleton")

抱歉,不是要问你为什么要单身吗?而是我对这种方法提出了质疑:您使班级成为单身人士,并要求Spring将其设为单身人士。您只需要一个。在本文中,他同时做这两项,然后忽略了Spring。

np。感谢您的投入。我试图针对两件事:1.我希望Spring初始化单例。因此,如果初始化失败,则应用程序加载失败。 2.我希望其他类能够使用类而不必依赖contextAwareObj.getBean("MySingleton")。

如果没有使用Spring bean的类,那么您无需在应用程序上下文中将其声明为单例。 Spring将创建一个,因为" lazy-init"为false,然后再也不需要创建另一个。另外,还要确保在bean定义之前放置一个注释块,以声明该bean永远都不应被引用,并且那里有初始化失败的地方。否则,以后有人可能会删除不需要的内容。

您出于其他目的滥用Spring Bean,如果希望在启动时发生某些事情,请查看如何注册在sprong启动时运行的方法。不要定义不应注入的bean。

真正的单身人士很难上班。


最好的选择就是简单地做到这一点

public class MySingleton {
private static MySingleton INSTANCE = new MySingleton();

也就是说,如果您的真实代码中没有任何构造函数参数。

volatile行为已在JDK 5.0及更高版本中修复,因此您可以再次将其与双重检查锁定一起使用

每当我想避免麻烦时,都会使用这种模式(即没有构造函数)。

恕我直言,这是编写Singleton的唯一真正安全的方法,但这使OP问题变得有些偏离主题。注意,要真正锁定它,构造函数应该是私有的,然后必须提供一个静态的" getInstance()"或类似的方法(这实际上与现代软件框架中的Bean模式混为一谈)。

在我看来,这是一个解决方案。

如果创建一个bean并在配置中将其声明为单例,那么就无需保护bean避免被多次创建。

现在,您基本上可以保护自己免受错误配置Bean的侵害。

我个人将通过spring配置和Javadoc中的文档"解决"该问题。

要在启动时运行代码(并在发生错误时失败),请使用多种注册启动事件的方式之一,例如参见http://www.baeldung.com/running-setup-logic-on-startup-in-spring

例:

@Component
public class InitializingBeanExampleBean implements InitializingBean {
private static final Logger LOG = Logger.getLogger(InitializingBeanExampleBean.class);
@Autowired
private Environment environment;
@Override
public void afterPropertiesSet() throws Exception {
LOG.info(Arrays.asList(environment.getDefaultProfiles()));
}
}