使单例成为Spring bean的正确方法

时间:2011-06-01 17:19:15

标签: java spring singleton

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

使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将被定义为:

<bean id="MySingletonBean"
    class="com.MySingleton"
    factory-method="getInstance" lazy-init="false" singleton="true">
</bean>

注意: 其中大部分与本文中讨论的策略类似: http://springtips.blogspot.com/2007/06/configuration-hell-remedy-with.html

<小时/> 修改1:

使用这个单例的类不是spring bean本身..它们只是非弹簧pojos,我无法转换为spring。他们必须依赖getInstance()方法获取Singleton。


编辑2:(将我在下面发表的评论复制到此说明部分)  我试图针对两件事:

  1. 我希望Spring初始化单例。所以,如果 初始化失败,然后应用程序加载失败。
  2. 我希望其他类能够使用类而不必依赖contextAwareObj.getBean(“MySingleton”)
  3. <小时/> 编辑3(最终): 我决定让这个班级成为一个单身人士......而不是把它变成一个春天的豆子。如果它无法初始化,它将在日志文件中记录一些内容..希望进行部署的人注意到....我放弃了我之前提到的方法,因为我觉得它将来会造成维护噩梦,所以我不得不选择 - 单身 - 或 - 春豆。我选择了单身人士。

7 个答案:

答案 0 :(得分:18)

必须INSTANCE字段声明为volatile才能使双重检查锁定正常工作。

请参阅Effective JavaItem 71

答案 1 :(得分:12)

为什么你首先使用 singleton 模式?让Spring为你创建bean(使用默认的singleton范围)并使用它。当然总是有人可以手工制作豆子,但在我的情况下这绝不是问题。

依赖注入和Spring管理的bean生命周期将显着减轻您的生活(只需看看您可以避免多少陷阱)。另请注意,从c-tor或@PostContruct方法抛出的异常将传播并导致应用程序上下文启动失败。

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

@Service
public class Singleton {

    private static AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();

    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注入所有内容。

答案 2 :(得分:6)

我不确定你为什么要这样做。当你告诉Spring一个bean应该是一个单例时,相应的类不需要是一个单独的,也不需要工厂。 Spring只是简单地创建了一个实例。

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

答案 3 :(得分:5)

真正的单身人士很难开始工作。

挥发性双重检查锁定也不起作用。在维基http://en.wikipedia.org/wiki/Double-checked_locking

上阅读相关内容

你最好的选择就是这样做

public class MySingleton {

    private static MySingleton INSTANCE = new MySingleton();

即如果您的实际代码中没有任何构造函数参数。

答案 4 :(得分:3)

据我所知,这是一种腰带和吊带的解决方案。

如果你创建一个bean并在配置中将它声明为一个单例,那么就不需要保护bean不被多次创建。

你现在基本上保护自己免受错误配置bean的人的伤害。

我个人会通过spring配置和Javadoc中的文档“解决”它。

答案 5 :(得分:1)

要在启动时运行代码(并在出错时失败),请使用多种方法之一来注册启动事件,例如:见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()));
    }
}

答案 6 :(得分:0)

        @Component
        public class SingletonDAOImpl {
        
        }
        
        @Component
        public class SingletonDAO {
        
        @Autowired private SingletonDAOImpl instance;
        
        public SingletonDAOImpl getInstance(){
        return this.instance
        }
        
        }
    
    public class WhateverPlaceYouNeedIt{
    @Awtowired private SingletonDAO  singletonDao;
    
    public void useSIngleton() {   
    SingletonDAOImpl INSTANCE = singletonDao.getInstance();
}
    }

我尝试了很多方法来做类似 SingletonDao.instance.doSomething() 的事情 但只是不是在春天的方式,你会发现很多黑客来做到这一点,但在我看来是不正确的

这里

  • 你有你的 Singleton,它可以在 Multiton 之后改变
  • 肯定是单一实现
  • 将 INSTANCE 模式视为“getInstance”
  • 在内存中,所以每次都是与单例中相同的对象

是同一个原理应用略有不同,很简单,一直尝试KIS实现(Keep it simple)