So Long Spring XMLs

Like many java projects these days, we use Spring in Outbrain for configuring our java dependencies wiring. Spring is a technology that started in order to solve a common, yet not so simple, issue – wiring all the dependencies in a java project. This was done by utilizing the IoC (Inversion of Control) principles. Today Spring does a lot more than just wiring and bootstrapping, but in this post I will focus mainly on that.

When Spring just started, the only way to configure the wirings of an application, was to use XMLs which defined the dependencies between different beans. As Spring had continued to develop, 2 more methods were added to configure dependencies – the annotation method and the @Configuration method. In Outbrain we use XML configuration. I found this method has a lot of pain points which I found remedy to using spring @Configuration

What is this @Configuration class?

You can think of a @Configuration class just like XML definitions, only defined by code. Using code instead of XMLs allows some advantages over XMLs which made me switch to this method:

  1. No typos – You can’t have a typo in code. The code just won’t compile
  2. Compile time check (fail fast) – With XMLs it’s possible to add an argument to a bean’s constructor but to forget to inject this argument when defining the bean in the XML. Again, this can’t happen with code. The code just won’t compile
  3. IDE features come for free – Using code allows you to find usages of the bean’s constructor to find out easily the contexts that use it; It allows you to jump back and forth between beans definitions and basically everything you can do with code, you get for free.
  4. Feature flags – In Outbrain we use feature-flags a lot. Due to the continuous-deployment culture of the company, a code that is pushed to the trunk can find itself in production in a matter of minutes. Sometimes, when developing features, we use feature flags to enable/disable certain features. This is pretty easy to do by defining 2 different implementations to the same interface and decide which one to load according to the flag. When using XMLs we had to use the alias feature which makes it not intuitive enough to create feature-flags. With @Configuration, we can create a simple if clause for choosing the right implementation.

Our example case

So, let’s start with a simple example of a Spring XML, and migrate it to Spring @Configuration class:
<?xmlversion="1.0"encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
                      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <import resource="another-application-context.xml"/>

  <bean id="someBean" class="avi.etzioni.spring.configuration.SomeClassImpl">
    <constructor-arg value="${some.interesting.property}" />
  </bean>

  <bean id="anotherBean" class="avi.etzioni.spring.configuration.AnotherClassImpl">
    <constructor-arg ref="someBean"/>
    <constructor-arg ref="beanFromSomewhereElse"/>
  </bean>
</beans>

Step 1: Migrate <beans> to @Configuration

In XMLs the highest tag in the hierarchy is <beans>. This tag will be replaced with a class, annotated with @Configuration:
@Configuration
public class ByeXmlApplicationContext {

}

Step 2: Create a method for each Bean

Each <bean> tag in the XML will be replaced with a method that’s annotated with @Bean annotation. Usually it would be a better practice for the method to return an interface type as follows:
@Configuration
public class ByeXmlApplicationContext {

  @Bean(name = "someBean")
  public SomeClass getSomeClass() {
      return new SomeClassImpl(someInterestingProperty);
  }

  @Bean(name = "anotherBean")
  pubic AnotherClass getAnotherClass() {
     return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
  }
}

A few things to notice:

  • Each method is defined to return an interface type. In the method body we create the concrete class.
  • The name that’s defined in the @Bean annotation is the same as the id that is defined in the XML for the beans.
  • The bean anotherBean is injected with someBean in the XML. In the scenario here, we just call the getSomeClass() method. This doesn’t create another bean, this just uses the bean someBean (the same as it was in the XML).

We notice that we’re missing the property someInterestingProperty and the bean beanFromSomewhereElse.

Step 3: Import other XMLs or other @Configuration classes

The bean beanFromSomewhereElse comes from a different XML file named another-application-context.xml and which was imported in the original XML. In order to use it, we need to import this XML here as well. To do so, we’ll just annotate the class with the annotation @ImportResource as follows:
@ImportResource("another-application-context.xml")
@Configuration
public class ByeXmlApplicationContext {
  . . .
}
That’s in fact equivalent to the <import resource=””/> tag in the XML format.
If this bean resides in another @Configuration class you can use a different annotation @Import to import it:
@Import(OtherConfiguration.class)
@Configuration
public class ByeXmlApplicationContext {
 ...
}

In order to complete the picture, here’s how you can import a @Configuration class from an XML configuration file:

<context:annotation-config/>
<bean class="some.package.ByeXmlApplicationContext"/>

The <context:annotation-config/> needs to be defined once in the context in order to make spring aware to @Configuration classes

Step 4: Import beans from other XMLs (or @Configuration class, or @Component etc… classes)

In order to use beans that were not defined in this @Configuration class we can either declare a private member annotated with @Autowired and @Qualifier as follows:
  @Autowired
  @Qualifier(value = "beanFromSomewhereElse")
  private final StrangeBean beanFromSomewhereElse;
This member can now be used to construct the bean anotherBean.
Another option is to declare a method argument to getAnotherClass() as follows:

  @Bean(name = "anotherBean")
  pubic AnotherClass getAnotherClass(@Qualifier (value = "beanFromSomewhereElse")
    final StrangeBean beanFromSomewhereElse) {
     return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
  }

I usually prefer the first method as it is less verbose. But of course, that’s just a matter of taste.

Just remember – the beans you import must be loaded to the application context – either by @Import or @ImportResource from this class, or using any other method from anywhere else (XML, @Configuration or annotations).

Step 5: Import properties

So, we still need to import somehow the property someInterestingProperty which was defined in the XML using ${some.interesting.property}. Well, that will be very similar to autowiring a bean, but instead of the @Qualifier annotation, we’ll use the @Value annotation:
@Autowired
@Value("${some.interesting.property}")
private final String someInterestingProperty;
You can also use a SpEL (Spring Expression Language) expressions with @Value.

Step 6: Import @Configuration from web.xml

At the final step, we would like to be able to import an entire application-context without using any XMLs. If we have a web app, this can be done by declaring the class in the web.xml as follows:

 

<servlet>
    <servlet-name>my-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextClass</param-name>
      <param-value>
        org.springframework.web.context.support.AnnotationConfigWebApplicationContext
      </param-value>
    </init-param>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>some.package.ByeXmlApplicationContext</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

Summary

As you can see – spring @Configuration classes can be a powerful tool in defining your application context. But with great power comes great responsibility. Code is much easier to abuse than XMLs. It’s easy to make complexed @Configuration classes. Try to think of the @Configuration class as a more flexible XMLs and behave them as if they were XMLs:
  • Split to different @Configuration classes and don’t put all of your beans in one class
  • Give meaningful names and even decide on a naming convention
  • Avoid any logic inside the @Configuration classes. Aside maybe for things like feature-flags.
Of course, I just gave here the basics. The internet is full of resources about using @Configuration classes. And of course, you are more than welcome to contact me for any further help. I’ll do my best to assist.

This post is based on a tech-talk I gave in Outbrain. You can find the slides here.

1 Comments
  1. Dan

    Still .., painful framwork to work with, you think you understand stuff until you run into wiring issues, dependencies and integration nightmare. IMO, Overall Java web dependencies are painful, wasting time for cumbersome, verbose thing just to achieve simple thing, you’ll always work under pressure. Nowdays, framework should be easy.

    Have a look at PHP and Python’s frameworks/libraries, how easy and fast they are to use. Compare to them you’ll end up unproductive with Spring framework and overall Java web stuff.

    Of course this is just my opinion.

Leave a Reply

Your email address will not be published.