Injecting Spring 2.5 beans into Stripes Actions

Christian Nelson ·

I’ve been playing around with Stripes, a light-weight, well-designed simple Java web MVC framework recently. I haven’t had the pleasure of working with it on a production application yet, but hope to sometime soon. Meanwhile, I’ve been tinkering on a pet project.

As you may know, we often use Spring for lifecycle management of our services and dependency injection (among other uses). In a Stripes + Spring application, you can imagine Spring-managed services being used by Stripes Actions (i.e. controllers). Every incoming HTTP request results in a new instance of a Stripes Action, thus the newly created actions must have their dependencies injected for every request.

Stripes ships with a SpringInterceptor that supports annotating fields and methods on your actions with @SpringBean. While this works fine, I was pretty interested in being able to use Spring 2.5’s annotations for marking what should be injected on my actions, so I created a Spring25Interceptor (see code below).

The Spring25Interceptor is configured the same way as the out-of-the-box SpringInterceptor. In your web.xml:

web.xml

...
<filter>
    <display-name>Stripes Filter</display-name>
    <filter-name>StripesFilter</filter-name>
    <filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
    <init-param>
        <param-name>Interceptor.Classes</param-name>
        <param-value>
            net.sourceforge.stripes.integration.spring.Spring25Interceptor
        </param-value>
    </init-param>
    ...
</filter>
...

Since it uses the same annotations that Spring 2.5’s supports (@Autowired, @Resource, and @Qualifier), annotating your Stripes Actions is easy and should look pretty familiar (see Juergen’s blog for a comprehensive overview). Here are a few hypothetical examples:

LandingActionBean.java

public class LandingActionBean extends AbstractActionBean
{
    // Autowire by type (looks for a bean in the application context of type ServiceA)
    @Autowired ServiceA serviceA;

    // Autowire by name (looks for a bean with the name 'serviceB')
    @Resource ServiceB serviceB;

    // Autowire by name (looks for a bean with the name 'serviceC')
    @Autowired @Qualifier("serviceC") ServiceC serviceCee;

    // Method injection examples (all of the above can be applied to methods as well)

    @Autowired
    public void setServiceA(ServiceA a) { this.serviceA = a; }

    @Autowired
    public void setServices(ServiceA a, ServiceB b, ServiceC c) {
        this.serviceA = a;
        this.serviceB = b;
        this.serviceC = c;
    }

    @DefaultHandler
    public Resolution execute()
    {
        return new ForwardResolution("/landing.jsp");
    }
}

While it may be considered a subtle improvement, I really like the fact that with this new interceptor, my application has a consistent syntax for dependency injection across the tiers. Additionally, the semantics of the Spring annotations are also consistent and shared throughout. An added bonus is that the new Interceptor is significantly smaller than the existing one.

Here’s the Interceptor code that does the dependency injection into actions:

Spring25Interceptor.java

import net.sourceforge.stripes.action.*;
import net.sourceforge.stripes.controller.*;
import net.sourceforge.stripes.util.*;
import org.springframework.beans.factory.config.*;
import org.springframework.context.*;
import org.springframework.util.*;
import org.springframework.web.context.support.*;
import javax.servlet.*;

@Intercepts(LifecycleStage.ActionBeanResolution)
public class Spring25Interceptor implements Interceptor
{
    private static final Log log = Log.getInstance(Spring25Interceptor.class);

    public Resolution intercept(ExecutionContext context) throws Exception
    {
        Resolution resolution = context.proceed();
        log.debug("Running Spring dependency injection for instance of ", context.getActionBean().getClass().getSimpleName());
        ServletContext servletContext = StripesFilter.getConfiguration().getServletContext();
        ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();
        beanFactory.autowireBeanProperties(context.getActionBean(), AutowireCapableBeanFactory.AUTOWIRE_NO, false);
        beanFactory.initializeBean(context.getActionBean(), StringUtils.uncapitalize(context.getActionBean().getClass().getSimpleName()));
        return resolution;
    }
}

I’ve created a enhancement request for this feature, though the comment-thread is a little bit all over the place. You can find the latest version of the code and Javadoc as an attachment on the issue.

Christian Nelson
Christian Nelson

Christian is a software developer, technical lead and agile coach. He's passionate about helping teams find creative ways to make work fun and productive. He's a partner at Carbon Five and serves as the Director of Engineering in the San Francisco office. When not slinging code or playing agile games, you can find him trekking in the Sierras and playing with his daughters.