Introduction
I generally try to avoid traditional Gang of Four Design Pattern Singletons. In Java they are only guaranteed to be singular per running Java Virtual Machine, don't scale to a clustered environment, can be serialized / deserialized to create a duplicate even in the same JVM and are generally the OOP version of global variables.However, in a given project I had an exception to this rule. I had an object that was using a single TCP port going into an environment where only one instance would be deployed per machine so I thought to myself that I would use the Singleton pattern.
Background
Created at imgflip, original image copyright Dos Equis. |
Hypothesis
"Verify that Spring is treating Singletons correctly because it's been unreliable, surprising and buggy in the past." In code-form it was a standard Spring @Service in the default Singleton scope with a check in the constructor (see Further Reading for other reasons to do this) to blow up if the constructor is called twice. In addition, Spring makes you have a public constructor even for your Singletons so blowing up can avoid programmer error of someone later trying to just callnew ExampleSingleton
.@Service // implied Spring Singleton Scope public class ExampleSingleton { // should only be called once by Spring public ExampleSingleton() { if (instance != null) { throw new IllegalStateException("Singleton constructor called twice!"); } } }
Results
As you may have guessed from the image, Spring was attempting to create the "Singleton" repeatedly and blowing up with the IllegalStateException. From doing some research I found that @ComponentScan was behaving strangely. It turns out that if you have multiple Spring @Configuration classes and if they overlap on a package (e.g.@ComponentScan(basePackages = {"com.yourcompany"})
and another with @ComponentScan(basePackages = {"com.yourcompany.utils"})
, Spring will (re)create all of the Singletons in the overlap (in this example com.yourcompany.utils
) twice. I found this surprising and very strange. In addition, the same link mentions that Spring only promises that a Singleton will happen once per ApplicationContext, and Servlets usually have more than one.Conclusion
I concluded that you can't trust Spring to honor the@Singleton
contract and to manage my instances myself. After doing more research I found that in Java the best way is to have a SingletonFactory. I ended up having a base class called SpringSingleton
, which has the documentation on why all of this is necessary and a protected constructor that keeps track of which sub-classes have been instantiated with a Set<SpringSingleton>
that is accessed in a synchronized block. In addition the @Configuration
classes have an @Autowired SingletonFactory
and @Bean
definitions that get the beans from the SingletonFactory
. All in all it still isn't foolproof for Serializable
Singletons (see below) but does a great job otherwise. There is more complexity (4 classes for the simple case) but new Singletons end up being easy to implement and work correctly. I would post the code but I made it at work so the company has copyright.Too bad I couldn't just have
@Component
on a class with the default scope and have it work right.
No comments:
Post a Comment