Sunday, May 22, 2011

Spring beans and annotations in servlet with aspectj

Sometimes you have limited set of technologies to use on your project (for example you cannot go beyond already approved technology stack or you have very conservative team lead/manager etc).
In these cases usage of techniques like this or this is a good solution to go.
But i've noticed that lots of people with total freedom of technologies to choose are still using solutions like mentioned before or some self-made variation of these.
I believe that if there is a ready, tested and well-known solution to your problem - you should this solution and not to invent another bicycle.
In case of using Spring in a servlet (GWT RPC or any other) this solution is aspectj.

Here is a short how-to use Spring in a servlet. I will use Maven but you can easily adapt this to what you are using for dependency management and/or build.

First, we need to define required dependencies in POM file:
<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>3.0.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.6.10</version>
    </dependency>
</dependencies>
and add aspectj compiler phase:
<build>            
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
Then create Spring config file that will tell Spring to manage classes created outside of Spring context:
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:annotation-config/>
    <context:spring-configured/>
</beans>
Last thing you need is to add org.springframework.beans.factory.annotation.Configurable annotation to servlet class.
Afterwards you can simply use @Autowired annotation on servlet fields.
@Configurable
public class Servlet extends HttpServlet {
    @Autowired
    private Bean bean;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        bean.doSomething();
    }
}
@Transactional annotations should be configured in Spring context XML like following: <tx:annotation-driven mode="aspectj"/>, and the same with @Secured.
Of course you will need to add some HTTP filter that will translate exceptions to user-readable format but this is out of scope of this article.