Using Spring managed Bean in non-managed object
We have to deal with legacy code, even when we would like to use the best and newest technologies available. Imagine the new code is written with the newest technologies of the Spring Framework and the legacy code is not written in Spring at all. Then using Spring managed Beans in non-managed Spring objects is one of the patterns we have to deal with. The legacy code has non-managed Spring objects, while the code we want to reference to is a Spring managed Bean. How do we solve this problem?
Create a Spring Bean
Let's assume we have a managed Spring Bean called TaxService
and an object called LegacyObject
. The LegacyObject
is the legacy code from where we would make a reference to the method calculateTax
on the managed Spring Bean.
package com.jdriven;
import org.springframework.stereotype.Service;
@Service
public class TaxServiceImpl
implements TaxService {
@Override
public Double calculateTax(Double price) {
return new Double(price \* 0.21);
}
}
Interface with bridged service methods
We define an interface which contains a list of methods. Each of these methods return a Spring managed Bean. We create a method called getTaxService
to return our just created TaxService
Bean.
package com.jdriven;
/\*\*
\* This interface represents a list of Spring Beans (services) which need to be referenced from a non Spring class.
\*/
public interface SpringContextBridgedServices {
TaxService getTaxService();
}
Implement the Spring Context Bridge
Next, we create an implementation for the SpringContextBridgedServices
interface. Let's call this class SpringContextBridge
and make it a Spring Bean and add the following functionality in the class.
- This class should also implement the
ApplicationContextAware
interface from Spring. The only argument in the method we need to implement from the interface is the argumentApplicationContext
. We hold this argument in a static member variable. - Create a static method to return the
SpringContextBridgedServices
and let this method return the Bean which is managed by Spring. UseapplicationContext.getBean(SpringContextBridgedServices.class)
to return it. - Autowire the
TaxService
and return it in the method we need to implement from theSpringContextBridgedServices
method.
package com.jdriven;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/\*\*
\* Register this SpringContextBridge as a Spring Component.
\*/
@Component
public class SpringContextBridge
implements SpringContextBridgedServices, ApplicationContextAware {
private static ApplicationContext applicationContext;
@Autowired
private TaxService taxService; //Autowire the TaxService
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
/\*\*
\* A static method to lookup the SpringContextBridgedServices Bean in
\* the applicationContext. It is basically an instance of itself, which
\* was registered by the @Component annotation.
\*
\* @return the SpringContextBridgedServices, which exposes all the
\* Spring services that are bridged from the Spring context.
\*/
public static SpringContextBridgedServices services() {
return applicationContext.getBean(SpringContextBridgedServices.class);
}
@Override
public TaxService getTaxService() {
return taxService; //Return the Autowired taxService
}
}
- Note 1: It is a possibility to return a Spring managed bean in a static method itself. I choose not to do this, so I have less static methods and can later Mock some of the references Services.
- Note 2: Eventually you would like to split up the two functionalities. One holding the
ApplicationContext
and returning theSpringContextBridgedServices
Bean. The other being theSpringContextBridgedServices
Bean itself. In this short demo I just put them in the same Bean.
Take me to the Bridge
Now it's time to call the bridge. This is as simple as demonstrated in the code below.
package com.jdriven;
public class LegacyObject {
private Double price;
public Double doTheCalculation() {
//Get the Service from the Bridge
TaxService taxService = SpringContextBridge.services().getTaxService();
return taxService.calculateTax(this.price);
}
}
Flexible but non-restricted alternative
This is a way in which the list of bridged services is restricted. Only the services mentioned in the SpringContextBridgedServices
interface will be bridged. If you want a more flexible but less controlled way, you can rewrite the SpringContextBridgedServices
.
package com.jdriven;
public interface SpringContextBridgedServicesAlternative {
<T> T getService(Class<T> serviceType);
}
Now we can get a service by calling SpringContextBridge.services().getService(TaxService.class)
. In this alternative we have no control over which Spring managed Bean may be bridged.