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.

  1. 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 argument ApplicationContext. We hold this argument in a static member variable.
  2. Create a static method to return the SpringContextBridgedServices and let this method return the Bean which is managed by Spring. Use applicationContext.getBean(SpringContextBridgedServices.class) to return it.
  3. Autowire the TaxService and return it in the method we need to implement from the SpringContextBridgedServices 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 the SpringContextBridgedServices Bean. The other being the SpringContextBridgedServices 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.

shadow-left