Using Spring with the Play Framework (2.2.x)

In order to develop modular Java applications, the use of a dependency injection framework is essential. In this post I show how to combine Spring with Play Framework: setting it up, how to use it for action composition and in unit tests and issues that I have found.

Note: I’m using Play framework version 2.2.x and Spring 3.2.5

Library dependency

Add Spring dependency to your project in build.sbt:

libraryDependencies ++= Seq(
    ...
  "org.springframework" % "spring-context" % "3.2.5.RELEASE",
  "org.springframework" % "spring-test" % "3.2.5.RELEASE",
    ...
)

In /conf folder add components.xml Spring configuration.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <context:component-scan base-package="... auto wired beans ... "/>
    ... beans ...
</beans>

Controller

Since Play version 2.1 [^version2.1] controller don’t have to be static anymore, which makes it possible to inject them. In your Global.java class override following methods:

protected ApplicationContext ctx;

@Override
public void onStart(Application app)
{
    String springConfigurationName = "components.xml";
    ctx = new ClassPathXmlApplicationContext(springConfigurationName);
    Logger.debug("Loading spring configuration: {}", springConfigurationName);
}

@Override
public <A> A getControllerInstance(Class<A> clazz)
{
    return ctx.getBean(clazz);
}

Now you can use your controllers by annotating them (I prefer to call my Play controllers “Controllers” and put them in a different package than the default modules package suggested by the official Play documentation):

package api.controllers

@org.springframework.stereotype.Controller
public class MyController
{
    // your controller methods here
}

In order to enable Spring to wire your controller, you need to configure the base package in components.xml, for example, if the package is named api.controllers you could set it up by adding:

<beans xmlns=...
    <context:component-scan base-package="api"/>
</beans>

@Autowired beans

You can now wire your bean into your classes. I prefer to put most business logic into services and wire them as needed.

@org.springframework.stereotype.Controller
public class BoardController extends Controller
{
    @Autowired
    private ServiceA serviceA;

    @Autowired
    private ServiceB serviceB;

    // controller methods
}

@org.springframework.stereotype.Service
public class ServiceA
{
    // service methods
}

Action composition

One important feature of Play is action composition 1. As Action are going to get wired in it is important to set the correct bean scope2. If no scope is specified, Spring will use the default one which is “singleton”, which will cause weird behavior. Actions need to be created with each instance, so “prototype” is the correct one:

@Scope("prototype")
@Component
public class MyAction extends Action.Simple
{
    // Action methods
}

Unit tests

In order to run unit tests with wired beans, we need to annotate our spring configuration and class runner:

@ContextConfiguration(locations = {"classpath:/components.xml"})
public class BaseTest extends WithApplication
{
    // common test methods
}

@RunWith(SpringJUnit4ClassRunner.class)
public class SomeTest extends BaseTest
{
    @Test
    public void aTest()
    {
        // ....
    }
}

Issues

Last updated: February 2014

Using Play and Spring together, you may encounter following (strange) error message:

java.lang.VerifyError: Stack map does not match the one at exception handler

This seems to happen when using injected classes in a try-catch context. A solution for me was to set _JAVA_OPTIONS like this:

export _JAVA_OPTIONS="-XX:-UseSplitVerifier"

More about this bug can be found here: https://github.com/playframework/playframework/issues/1966

With this, I hope you will be able to use Spring in your Play project and keep it nicely modularised.

Thanks for reading

I hope you enjoyed reading this post. If you have comments, questions or found a bug please let me know, either in the comments below or contact me directly.

Resources

Subscribe to Human Intelligence Engineering
Explorations of human ingenuity in a world of technology.