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.

The next code highlights this behavior:

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.

The next code snippet shows how to use the @Lookup annotation:

The problem with this approach is that it will only work with @Component annotated beans instantiated using component scan or autodetection. It won’t work with @Bean annotated beans.

The next test shows the behavior of the @Lookup annotation in a @Component singleton Bean:

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:

The previous test will fail, as the getSampleUsingLoopup() won’t be overridden, thus returning null. We can check this behavior with this modified test:

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.

We can declare the Singleton scoped class as follows:

We can test the expected behavior using the following code snippet:

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.

The next test shows the described expected behavior:

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

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.

The full source code for this post can be found at GitHub.

Leave a comment

Your email address will not be published. Required fields are marked *