Meta

Spring Bean Scopes: Singleton with Prototypes

Introduction

This is the second post on the series about Spring Bean Scopes. In the previous tutorial we saw that there were issues rising when a Prototype scoped Bean was injected in a Singleton scoped Bean. The main problem is that autowired Prototypes will be injected when the Singleton Bean is instantiated (which happens only once) thus even though they are prototypes in reality they’ll behave as singletons.

In the previous example, although the object requested with getAutowiredSample is defined with a Prototype scoped Bean, the instance of the object returned in both requests is the same.

To overcome this behavior there are several options available.

@Lookup annotation

The @Lookup annotation allows us to annotate getter methods for a specific Bean. The Spring container will override these methods at runtime and redirect them to the BeanFactory performing a regular getBean call, returning whatever bean the getBean method would be returning (Singleton, Prototype, Request, Session…). In this case, as the defined Bean has a Prototype scope it will return a new instance for each Bean request.

As shown in the previous snippet, this is the behavior we are expecting. Two Singleton Beans which are the same instance even if requested twice, return different instances of the Sample class which is defined in a Bean with Prototype scope.

There is a problem with this approach, as already stated, it will only work with Singleton Beans declared using the @Component annotation. If the Bean is declared using an approach similar to the following:

Using ApplicationContext

Another approach to overcome the scoped bean injection problem is to inject ApplicationContext into the Singleton scoped bean. Whenever an instance of a Prototype scoped bean is needed, the singleton should request it to the ApplicationContext instance.

Although this approach works, it clearly violates SOLID by contradicting the inversion of control design principle. Dependencies are requested directly to the Spring container instead of being injected to the class, so the code is tightly coupled to Spring. e.g. there’s no other way to run the previous unit test without using SpringJUnit4ClassRunner.

Spring’s ObjectFactory interface

Finally but, in my opinion, the cleanest way to solve the scoped bean injection problem is to use the Spring’s ObjectFactory interface. With this approach, instead of injecting the Bean itself we inject a factory for the Bean using this interface.

Instead of injecting the bean during the Singleton scope class instantiation, we inject an interface for a Bean ObjectFactory for a given type of Bean. Depending on the type and scope configured for the ObjectFactory, the getObject method will return an object instance accordingly.

Using ObjectFactory won’t violate any OOP principle as you can always inject it using a custom ObjectFactory implementation:

Autowired ObjectFactory

1

2

newClassWithSamplePrototypes(newSample(),

()->newSample());

Conclusion

This post shows how to solve the scoped bean injection problems that arises when injecting a Prototype scoped Bean into a Singleton scoped Bean. We’ve seen different approaches to solve the problem and the advantages and disadvantages for each of them.