Correct way of making a singleton a Spring bean

给你一囗甜甜゛ 提交于 2019-12-28 09:22:04

问题


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.

回答1:


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

See Effective Java, Item 71.




回答2:


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.




回答3:


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.




回答4:


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.




回答5:


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.




回答6:


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


来源:https://stackoverflow.com/questions/6205171/correct-way-of-making-a-singleton-a-spring-bean

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!