Swapping out Spring Bean Configuration at Runtime

Most Java developers these days deal with Spring on a regular basis and there are lots of us out there that have become familiar with its abilities as well as its limitations.

I recently came across a problem that I hadn’t hit before: introducing the ability to rewire a bean’s internals based on configuration introduced at runtime. This is valuable for simple configuration changes or perhaps swapping out something like a Strategy or Factory class, rather than rebuilding of a complex part of the application context.

I was able to find some notes about how to do this, but I thought that some might find my notes and code samples useful, especially since I can confirm this technique works on versions of Spring back to 1.2.6. Unfortunately, not all of us are lucky enough to be on the latest and greatest of every library.

Scope of the Problem

The approach I’m going to outline is meant primarily to target changes to a single bean, though this code could easily be extended to change multiple beans. It could be invoked through JMX or some other UI exposed to administrators.

One thing it does not cover is rewiring a singleton all across an application – this could conceivably be done via some reflection and inspection of the current application context, but is likely to be unsafe in most applications unless they have some way of temporarily shutting down or blocking all processing for a period while the changes are made all over the application.

The Code

Here’s the sample code. It will take a list of Strings which contains bean definitions, and wire them into a new temporary Spring context. You’ll see a parent context can be provided, which is useful in case your new bean definitions need to refer to beans already configured in the application.

public static <T> Map<String, T> extractBeans(Class<T> beanType,
   List<String> contextXmls, ApplicationContext parentContext) throws Exception {

   List<String> paths = new ArrayList<String>();
   try {
      for (String xml : contextXmls) {
         File file = File.createTempFile("spring", "xml");
         // ... write the file using a utility method
         FileUtils.writeStringToFile(file, xml, "UTF-8");
         paths.add(file.getAbsolutePath());
      }

      String[] pathArray = paths.toArray(new String[0]);
      return buildContextAndGetBeans(beanType, pathArray, parentContext);

   } finally {
      // ... clean up temp files immediately if desired
   }
}

private static <T> Map<String, T> buildContextAndGetBeans(Class<T> beanType,
               String[] paths, ApplicationContext parentContext) throws Exception {

   FileSystemXmlApplicationContext context =
      new FileSystemXmlApplicationContext(paths, false, parentContext) {
         @Override  // suppress refresh events bubbling to parent context
         public void publishEvent(ApplicationEvent event) { }
      };

   try {
      // avoid classloader errors in some environments
      context.setClassLoader(beanType.getClassLoader());
      context.refresh(); // parse and load context
      Map<String, T> beanMap = context.getBeansOfType(beanType);

      return beanMap;
   } finally {
      try {
         context.close();
      } catch (Exception e) {
         // ... log this
      }
   }
} 

If you look at buildContextAndGetBeans(), you’ll see it does the bulk of the work by building up a Spring context with the supplied XML bean definition files. It then returns a map of the constructed beans of the type requested.

Note: Since the temporary Spring context is destroyed, ensure your beans do not have lifecycle methods that cause them to be put into an invalid state when stopped or destroyed.

Here’s an example of a Spring context that might be used to rewire a component. Imagine we have an e-commerce system that does fraud checks, but various strategies for checking for fraud. We may wish to swap these from our service class without having to stop and reconfigure the application, since we lose business when we do so. Perhaps we are finding a specific abuse of the system that would be better dealt with by changing the strategy used to locate fraudulent orders.

Here’s a sample XML definition that could be used to rewire our FraudService.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
   <bean id="fraudStrategy" class="com.example.SomeFraudStategory">
      <!-- example of a bean defined in the parent application context that we can reference -->
      <property name="fraudRuleFactory" ref="fraudRuleFactory"/>
   </bean>
</beans>

And here is the code you could use to rewire your bean with a reference to the defined fraudStrategy, assuming you have it in a utility class called SpringUtils:

public class FraudService implements ApplicationContextAware {

   private ApplicationContext context;
   // volatile for thread safety (in Java 1.5 and up only)
   private volatile FraudStrategy fraudStrategy;

   @Override // get a handle on the the parent context
   public void setApplicationContext(ApplicationContext context) {
      this.context = context;
   }

   public void swapFraudStategy(String xmlDefinition) throws Exception {
      List<Sting> definitions = Arrays.asList(xmlDefinition);
      Map<String, FraudStrategy> beans =
         SpringUtils.extractBeans(FraudStrategy.class, definitions, context);
      if (beans.size() != 1) {
         throw new RuntimeException("Invalid number of beans: " + beans .size());
      }
      this.fraudStrategy = beans.values().iterator().next();
   }

}

And there you have it! This example could be extended a fair bit to meet your needs, but I think it shows the fundamentals of how to create a Spring context on the fly, and use its beans to reconfigure your application without any need for downtime.

Reference: Swapping out Spring Bean Configuration at Runtime from our JCG partners at the Carfey Software blog.

Related Articles :
Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

Leave a Reply


three + 6 =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books