21

I am converting a singleton to a Spring bean, so that if the singleton fails to initialize, then entire web application's spring context doesn't load properly.

The advantage of making the Spring context not load properly, is that people will take notice and fix the configuration during deployment itself. As opposed to using 'non-spring bean' singleton: when that throws exception during initialization, nobody notices.. until a actual user complains of missing functionality.

My changes are working as expected.. but I am not sure if I am doing the right thing.
Any thoughts?

The code looks like this:

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
    }

}

And in the spring config xml, the bean will be defined as:

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

Note: Most of this is similar to the strategy discussed in this article: http://springtips.blogspot.com/2007/06/configuration-hell-remedy-with.html


Edit 1:

The classes that use this singleton, are not spring beans themselves.. they are just non-spring pojos, that I can't convert to spring. They must rely on getInstance() method get hold of the Singleton.


Edit 2: (copying a comment I made below into this description section) I am trying to target two things:

  1. I want Spring to initialize the singleton. So that if the initialization fails, then the application loading fails.
  2. I want the other classes be able to use classes without having to rely on contextAwareObj.getBean("MySingleton")


EDIT 3 (FINAL): I decided to make this class a singleton.. and am not making it a spring bean. If it fails to initialize, it will log something in the Log file.. hopefully the person doing deployment takes notice.... I abandoned the approach I mentioned earlier because I feel it will create a maintenance nightmare in future, so I had to pick between - singleton - or - spring bean. I chose singleton.
Crowie
  • 3,220
  • 7
  • 28
  • 48
rk2010
  • 3,481
  • 7
  • 27
  • 39
  • 1
    I've seen this double-checking approach frowned upon. try using an `enum`. – mre Jun 01 '11 at 17:24
  • 3
    the singleton scope is the default but otherwise it looks fine. I usually don't bother explicitly implementing singleton pattern (e.g. getInstance()) in spring apps since beans created by the container are singleton by default anyway. – Kevin Jun 01 '11 at 17:24
  • 1
    @Kevin. I can't get away from getInstance(). Because the classes that use this Singleton, aren't spring beans at all. This Singleton will be used by classes that are spring beans, non-spring beans, static methods of random classes.. – rk2010 Jun 01 '11 at 17:28
  • @Kevin is correct in that beans are singletons by default, however; your definition is incorrect. It should be `scope="singleton"`, since this is the default however it can be eliminated with the definition being reduced to `` – Brett Ryan Mar 20 '13 at 06:16

7 Answers7

19

You must declare the INSTANCE field as volatile for double-checked locking to work correctly.

See Effective Java, Item 71.

Community
  • 1
  • 1
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • Wouldn't moving the `synchronized` block outwards to include the is-null check (and remove the redundant check) also work? (Although I suppose if you expect code to call `getInstance()` a *lot*...) – Darien Jun 01 '11 at 18:10
  • 1
    @Darien that's exactly it. The point of the double-checked lock is to avoid the synchronization unless it's actually needed. _"If you need to use lazy initialization for performance on an instance field, use the double-check idiom. This idiom avoids the cost of locking when accessing the field after it has been initialized (Item 67)."_ – Matt Ball Jun 01 '11 at 18:18
  • 1
    There's a better way of doing it than using "volatile" keyword which is broken for certain versions of Java. Please see: http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh – DenTheMan Mar 05 '12 at 22:18
  • 1
    An easier solution is to use [Guava's Memioze Supplier](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Suppliers.html#memoize(com.google.common.base.Supplier)) – Adam Gent Dec 22 '12 at 16:12
  • The "Double-Checked Locking is Broken" Declaration http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html – Darrell Teague Jan 25 '16 at 21:49
17

Why are you using singleton pattern on the first place? Just let Spring create bean for you (with default singleton scope) and... use it. Of course always somebody might create the bean by hand, but this was never a problem in my case.

Dependency injection and Spring-managed bean lifecycle will ease your life significantly (just see how many pitfalls you can avoid). Also note that exceptions thrown from c-tor or @PostContruct method will propagate and cause application context startup to fail as well.

UPDATE: I get your point. This is what came in to my mind:

@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();
    }

}

And let Spring do its job. You can use DI when possible and Singleton.getInstance() where you have to.

Also there are more hard-core solutions like compile-time AspectJ weaving and injecting Spring beans basically to everything.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • I am doing this so that I can avoid doing: `contextAwareObj.getBean("MySingleton");` A lot of the classes that use this MySingleton class, aren't spring beans.. and I can't inject any spring bean into them. So, either I use `contextAwareObj.getBean("MySingleton");` or I use the getInstance() method.. given that choice, I prefer using the getInstance() method. – rk2010 Jun 01 '11 at 17:35
  • your update looks like it could've worked.. but I am not totally certain. Anyways.. I chose to not make a spring bean... (see my Edit3 above). THanks. – rk2010 Jun 01 '11 at 18:46
  • Your advice of using a Spring-managed Singleton makes sense. Seldom is there a need where we don't want to let Spring manage our Singleton-like classes. Also, your snippet on using `AtomicReference` of the singleton instance is very helpful. Thanks for sharing! – asgs Jun 13 '15 at 17:50
7

I'm not sure why you'd want to do this. When you tell Spring that a bean should be a singleton, the corresponding class does not need to be a singleton, and does not need a factory. Spring just simply only ever creates one instance.

The linked article makes no sense to me, since there is NO injection happening, that I can see: "AnyService" is calling the singleton factory method; that the singleton is referenced in the app context is irrelevant until it's referenced, and it seems no other bean references it.

Rodney Gitzel
  • 2,652
  • 16
  • 23
  • I am making the spring bean a singleton, so that the other classes in the application don't have to do: `contextAwareObj.getBean("MySingleton")` – rk2010 Jun 01 '11 at 17:36
  • Sorry, didn't mean to question why you would want a singleton; rather I'm questioning the approach: you're making the class a singleton AND asking Spring to make it a singleton. You need only one. In the article, he does both, then ignores Spring. – Rodney Gitzel Jun 01 '11 at 17:39
  • np. appreciate the input. I am trying to target two things: 1. I want Spring to initialize the singleton. So that if the initialization fails, then the application loading fails. 2. I want the other classes be able to use classes without having to rely on `contextAwareObj.getBean("MySingleton")`. – rk2010 Jun 01 '11 at 17:45
  • If no classes are using the Spring bean, then you don't need to declare it a singleton in the application context. Spring will create one because "lazy-init" is false, and then never be asked to create another. Also ensure you put a comment block before the bean definition, stating that this bean is intended to never be referenced, and that it's there for initialization failure. Otherwise someone later may remove it as being not needed. – Rodney Gitzel Jun 01 '11 at 17:54
  • you are abusing Spring beans for a different purpose, if you want something to happen on startup, look how to register methods to run on startup of sprong. Do not define a bean that is not supposed to be injected. – tkruse Dec 05 '17 at 01:16
5

True singleton are hard to get working.

Volatile double-checked locking also does not work property. Read about it on wiki http://en.wikipedia.org/wiki/Double-checked_locking

Your best bet is to simply do this

public class MySingleton {

    private static MySingleton INSTANCE = new MySingleton();

That is if you do not have any constructor parameters in your real code.

Denis
  • 209
  • 3
  • 4
  • the volatile behaviour has been fixed with JDK 5.0 and above, so you are again free to use it together with the double checked locking – radio Feb 12 '14 at 14:46
  • I use this pattern (without constructor, that is) whenever I want to avoid hassles. – asgs Jun 13 '15 at 15:16
  • IMHO this is the only really safe way to write a Singleton but this is getting a bit off-topic from the OP's question. Note that to really lock this up, the constructor should be private and then a static "getInstance()" or similar method must be provided (which really messes with the Bean patterns in modern software frameworks). – Darrell Teague Jan 25 '16 at 21:52
3

According to me this is a belts-and-suspenders solution.

If you create a bean and declare it as a singleton in the configuration then there should be no need to protect the bean against being multiply created.

You are now basically protecting yourself from someone wrongly configuring the bean.

I personally would "solve" that by documentation in the spring configuration and Javadoc.

Peter Tillemans
  • 34,983
  • 11
  • 83
  • 114
1

To run code at startup (and fail on error) use one of the many ways to register startup events, e.g. see http://www.baeldung.com/running-setup-logic-on-startup-in-spring

Example:

@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()));
    }
}
tkruse
  • 10,222
  • 7
  • 53
  • 80
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();
}
    }

I tried in so many ways to do something like SingletonDao.instance.doSomething() but just is not in the spring way and you will find so many hacks in order to do this but is incorrect in my opinion

here

  • You have your Singleton, which can be changed after in a Multiton
  • For sure is a single implementation
  • is respecting the INSTANCE pattern as "getInstance"
  • Is in-memory so each time is the same object as in singleton

is the same principle applied slightly different, very simple, all the time try to KIS implementation(Keep it simple)