February 12, 2013

Java-based configuration in Spring - An introduction

If you have not started using Java-based configuration in your Spring projects you really should take a look at it since it is a very valuable tool to master. Java-based configuration has been supported right out of the box since Spring 3.0 so it has become quite complete by now. (with the stable release being 3.2.x at the time of writing this post)
Personally, I have found that I hardly ever use any XML configuration files in Spring projects that I create nowadays. And as a side note, if I am also able to use an application server that supports Servlet 3.0 I can even get rid of my web.xml file. Thus eliminating a big part of the XML-based configuration usually needed.

Ok, but why? You might ask. Well, if you think about it there are a lot of benefits of having your configuration in code instead of in XML.
Some of them are:
  • Your configuration will have the same power as your programming language giving you a lot more flexibility. Enabling you to take advantage of things like OO, and type safety.
  • You will get immensely more help from your IDE. Safe refactorings and easy navigation of your configuration are just some examples.
  • The configuration will be much clearer and easy to follow. Especially if you are writing libs/frameworks for others to use. No more searching for magic XML-files that you didn't even know existed. You can easily find any java class that is in your classpath.
If you're still not convinced then why not just give it a try. A warning though, it can be quite addictive once you have taken the red pill1.


This post is meant to be a short primer/reference on getting started with Java-based Spring configuration and contains a few examples of some common configurations to give you an idea of how they would look like in Java vs. XML. For a complete reference you should check out the Spring documentation that covers these topics pretty well. I will assume that you are at least somewhat familiar with working with Spring so that you have an idea of what is going on.


Defining the configuration class

Any class can be a configuration class. In order for Spring to know that it should treat a class as a configuration class we annotate it with @Configuration. For example, we can create a class called SpringConfig that we will use to replace our XML-file. The class would look like this:
@Configuration
@EnableWebMvc
@ComponentScan(basePackageClasses = {PackageMarker.class})
public class SpringConfig {
}
Also note that besides @Configuration I added two other annotations to this class: @EnableWebMvc and @ComponentScan. These two annotations corresponds to the following XML:
<mvc:annotation-driven />
<context:component-scan base-package="com.foo.bar.myapp" />
Assuming that PackageMarker is a "marker" interface defined in the com.foo.bar.myapp package.

Personally I am not a big fan of having magic strings sprinkled throughout the code so even if @ComponentScan("com.foo.bar.myapp") is the same as @ComponentScan(basePackageClasses = {PackageMarker.class}). I prefer the latter one because it refers to an actual class which helps to avoid typos and the IDE will also provide better support for refactorings etc.

To tell our web application to use Java-based configuration we can make our web.xml look something like this:
<servlet>
    <servlet-name>Spring MVC Dispatcher Servlet</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>com.foo.bar.SpringConfig</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

Note the use of AnnotationConfigWebApplicationContext and that the contextConfigLocation is pointing to our configuration class rather than an XML-file.

Our SpringConfig class is where we will put all of the methods that we will use to configure Spring. Now lets take a look at some typical configuration examples and see how they would look like in Java.

Defining beans

Defining a bean in Java is as simple as annotating a method with @Bean. The @Bean annotation corresponds to the <bean /> XML element so the following is effectively the same.

XML configuration:
<bean class="com.foo.bar.MyBean"/>
Java configuration:
@Bean
public MyBean myBean() {
    return new MyBean();
}

By default, the bean name will be the same as the name of the method.

A very common thing to do when building MVC applications is to define view resolvers. Lets do just that so we can see how to do a more realistic bean definition than the previous example. The standard XML configuration for adding view resolvers would typically look something like this:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/" />
    <property name="suffix" value=".jsp" />
</bean>
and the corresponding configuration in Java would be:
@Bean
public InternalResourceViewResolver internalResourceViewResolver() {
    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
    viewResolver.setPrefix("/WEB-INF/views/");
    viewResolver.setSuffix(".jsp");
    return viewResolver;
}

Custom MVC configuration

If you want to start customizing the MVC configuration you can extend the WebMvcConfigurerAdapter class and override the methods that you are interested in. Our configuration class would then look like this:
@Configuration
@EnableWebMvc
@ComponentScan(basePackageClasses = {PackageMarker.class})
public class SpringConfig extends WebMvcConfigurerAdapter {
}

Following are some examples of how you would override methods in WebMvcConfigurerAdapter.

Adding resource handlers

Standard XML configuration for defining where your static content is:
<mvc:resources location="/js/" mapping="/js/**" />
<mvc:resources location="/css/" mapping="/css/**" />
<mvc:resources location="/img/" mapping="/img/**" />
and corresponding Java-configuration:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/js/**").addResourceLocations("/js/");
    registry.addResourceHandler("/css/**").addResourceLocations("/css/");
    registry.addResourceHandler("/img/**").addResourceLocations("/img/");
}

Customizing message converters

You can control the message converters that will be used by your app by doing this in XML:
<mvc:annotation-driven>
    <mvc:message-converters register-defaults="false">
        <bean class="com.foo.bar.MyFactory" factory-method="createJSONMessageConverter()" />
        <bean class="com.foo.bar.MyFactory" factory-method="createConverterForHtmlResponses()" />
    </mvc:message-converters>
</mvc:annotation-driven>
To do the same in Java would look like this:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(createJSONMessageConverter());
    converters.add(createConverterForHtmlResponses());
}

Adding interceptors

Adding interceptors is also a quite common task and would look like this in XML:
<mvc:interceptors>
    <mvc:interceptor>
        <mapping path="/**"/>
        <exclude-mapping path="/web"/>
        <bean class="com.foo.bar.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>
and in Java:
@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/web");
}

And more...

There are of course a lot more you can do and the best way to get started is simply to start using Java-based configuration in your current project. Also keep in mind that depending on your needs, it might not always be possible, or convenient, to use only Java-based configuration. In that case it is good to know that you can mix Java and XML configurations perfectly well. You just have to decide if you want your configuration to be Java-centric or XML-centric and then adjust accordingly.

And the fact that you can use both XML-, and Java-based configuration at the same time makes it possible to do a gradual conversion of an existing project without being forced to rewrite all configuration at once.

Now go have fun with it!

------------
1) If you didn't get the red pill comment then just ignore it. It was just a nerdy movie reference.

No comments:

Post a Comment