Spring provides a lot of great ways to manage Java bean instantiation. While the most common method is singleton bean instantiation from a no-arg constructor, There are a variety of other ways. prototype scope allows a user to get a new instance of a bean every time getBean is called. constructor-arg allows arguments to be passed to constructors of a bean. factory-method allows a user to call a static method to return an instance of the static method’s declaring type. When combined with factory-bean, factory-method can be from an instance method (but then the type containing the method has to be a Spring managed factory). lookup-method allows an abstract class to be instantiated from the abstract lookup-method the same way one would create an anonymous class from an interface in Java. (Then the class and method have to be abstract however). What does one do however if even with all these options, it isn’t enough? What would one do if they wanted to have a singleton that returned instances whose type was based on a method parameter?
Last post I detailed the problem that led me down this path, but I will summarize here. I had set some basic criteria for any solution I came up with to solve my coding problem. To start, my Java code would not use any Spring specific interfaces or constructs. All Spring interfacing would happen in the Spring applicationContext, where the configuration had to be easy to understand. The reason for this is that I didn’t want to be dependent on a specific version of Spring if I didn’t have to. The specific problem was that I had a singleton bean CachingOsgiServiceGetter that would do the work of retrieving OSGi services. I had a method getServices that would retrieve a different type of Java Object instance (the OSGi service) based on parameters passed to that method. This class had to be configured once, preventing use of lookup-method. I didn’t want to use Spring libraries, so I couldn’t use factory-bean (org.springframework.beans.factory.FactoryBean). The method was an instance method, so I couldn’t use factory-method. The method needed a different parameter for each call to getService, so I couldn’t use constructor-arg on CachingOsgiServiceGetter, which would only allow me to configure it once. Finally, I couldn’t use prototype scope because I needed a different type for each call to getService, not the same type.
I solved this problem by combining my unchanged singleton bean CachingOsgiServiceGetter with factory-method, constructor-arg, and a helper static class. The keys here are that one, factory-method can return any type, not just the type of the method’s declaring class. two, constructor-arg can be used to pass any arguments to the factory-method. Three, While CachingOsgiServiceGetter.getService was an instance method that couldn’t be used as a factory-method, there was nothing preventing me from creating an adapter classs with static factory-method method that called my instance method!
The unchanged singleton bean:
public class CachingOsgiServiceGetter ... public <s> S getService(Class<s> clazz) ...
The factory-method adapter class (with static adapter method):
public class OsgiService {
public static <S> S getService(Class<S> clazz, CachingOsgiServiceGetter cachingOsgiServiceGetter) {
return cachingOsgiServiceGetter.getService(clazz);
}
}
The Spring applicationContext configuration:
<bean id="cachingOsgiServiceGetter"
class="com.github.mshin.sosa.CachingOsgiServiceGetter">
</bean>
<bean id="mySpringOsgiService"
class="com.github.mshin.sosa.OsgiService"
factory-method="getService">
<constructor-arg value="com.example.MySpringOsgiService" />
<constructor-arg ref="cachingOsgiServiceGetter" />
</bean>
<bean id="useServices"
class="com.example.osgi.service.use.spring.UseServices">
<property name="mySpringOsgiService" ref="mySpringOsgiService" />
</bean>
In the above example, getService is the instance method on my singleton bean (CachingOsgiServiceGetter) that returns the instances of Objects I want. OsgiService is my adapter class with a static adapter method for factory-method. I named the adapter OsgiService to represent the types getService could possibly return. UseServices is my singleton that needs to use the instance returned by getService.
So why not just declare the static factory-method in CachingOsgiServiceGetter? For one, it would be confusing to have CachingOsgiServiceGetter declared once as a singleton representing its actual type, then have it declared once for each time getService was called representing the OSGi Service type.
Don’t do this because it’s confusing!:
<bean id="cachingOsgiServiceGetter"
class="com.github.mshin.sosa.CachingOsgiServiceGetter">
</bean>
<bean id="mySpringOsgiService"
class="com.github.mshin.sosa.CachingOsgiServiceGetter"
factory-method="getService">
<constructor-arg value="com.example.MySpringOsgiService" />
<constructor-arg ref="cachingOsgiServiceGetter" />
</bean>
<bean id="useServices"
class="com.example.osgi.service.use.spring.UseServices">
<property name="mySpringOsgiService" ref="mySpringOsgiService" />
</bean>
Second, one doesn’t always have the ability to change the code of the singletons one’s trying to use. One does however always have the ability to create a static utility adapter class.
The code examples used in this post are simplified versions of deployable code that can be found in my code repository. Here are links to the application context, singleton bean that makes instances, and adapter class.