Spring AOP Advice on Annotated Controllers

| April 2, 2013 | 2 Comments

AOP DefinitionSpring AOP (Aspect-oriented programming) framework is used to modularize cross-cutting concerns in aspects. If you want a more simple definition you can think of them as a Interceptor but with more options configurations possible.  In Spring there are two different constructs that get called “interceptors”. First, there are Handler Interceptors, which are part of the Spring MVC framework (and similar to Interceptors in Struts 2), and give you the ability to add interceptor logic to requests.  But you also have Method Interceptors, which are part of the Spring AOP framework. These are much more general mechanism than Handler Interceptors, but also potentially more complex. In AOP terminology, such interceptors provide a means of coding the “aspects” you’re talking about.

In this article we are not going to cover how Spring AOP work, I am going to present the solution to an error that could appear when you want to use advices with controller methods in Spring 3 using annotation and non declarative configurations.

¿What is the problem?

You need to make some processing (before and/or after) the execution of a controller. You want to use the AspectJ annotation in Spring to do that. Your code could be something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller 
public class TestController { 
 
@RequestMapping("/test.fo") 
public String test(ModelMap model) 
 
{ 
 
model = new ModelMap(); 
 
return "test.jsp"; 
 
}
}

The aspect has to include an advice which will be executed before and after the controller is invoked by a request. Using annotation your aspect could be something similar to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Aspect
public class StateAspectImpl extends StateAspectI{
 
private static final Log LOG = LogFactory.getLog(StateAspectImpl.class);
 
@Pointcut("within(@org.springframework.stereotype.Controller *)") //we define a pointcut for all controllers
public void classPointcut() {}
 
@Pointcut("execution(* *getViewNew(..))") // the methods that ends in getViewNew are join to this pointcut
public void methodPointcut() {}
 
/**
* Operations*/
 
@Around("classPointcut() && methodPointcut() && args(request,modelMap)")
public ModelMap recoverStateView(ProceedingJoinPoint joinPoint, HttpServletRequest request, ModelMap modelMap) throws Throwable {
 
//the operations that you want execute
 
}

So you want to advice all the methods that contains getViewNew in the name from all classes annotated with @Controller. When you execute this code deploying the app in a server it doesn’t work and don’t raise any error or exception.

The cause is…

After debugging the code I realized that the advice is never been executed. So I tried to look for a solution in StackOverflow. Some of the question/answer that I find there were:

http://stackoverflow.com/questions/3310115/spring-aop-advice-on-annotated-controllers

http://stackoverflow.com/questions/789759/how-can-i-apply-an-aspect-using-annotations-in-spring

http://stackoverflow.com/questions/9310927/aspect-not-executed-in-spring

But anyone of these discussion and the solutions that are proposed in, works in my situation. The question that point me in the right direction was:

http://stackoverflow.com/questions/3991249/how-can-i-combine-aspect-with-controller-in-spring-3

The source of the problem is: Spring AOP is based on a proxy generation, that executed the advices in the pointcuts that you declare.For accomplish this, the classes that contain the methods that match with the pointcuts which you created (the classes we want to intercept the execution) have to be declared with component scanning in the domain on the DispatcherServlet.

At this point, if your using a xml file to describe the dispatched servlet, like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
 
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

for the web.xml file, and:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
<!-- not strictly necessary for this example, but still useful, see http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-controller for more information -->
<context:component-scan base-package="springmvc.web" />
 
<!-- the mvc resources tag does the magic -->
<mvc:resources mapping="/resources/**" location="/resources/" />
 
<!-- also add the following beans to get rid of some exceptions -->
<bean />
<bean
class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
</bean>
 
<!-- JSTL resolver -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
 
</beans>
for the spring servlet configuration file, the solution works without problem and the advice is executed before and after the method on the Controller classes.

But, what happen if you don’t have a dispatcher servlet XML file and you are using a class that extends WebMvcConfigurerAdapter to create the servlet configuration. In this case the advice is never executed because the controllers are not created in the domain of the DispatcherServlet.

The solution is…

There are two possible solutions to this problem:

  1. Change the configuration of your spring application to use a xml configuration instead of the WebMvcConfigurerAdapter.
  2. Create a aop xml file to declare the class that implement the advice and to configure the aop proxy to make use of this advice.
I recommended the second solution in the case in which you were using programmatic configuration of Spring instead of declarative. In that case the xml file could be:
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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:aop='http://www.springframework.org/schema/aop'
xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd'>
 
<!-- AOP support -->
<bean id='stateAspectImpl' class='...ui.aspect.StateAspectImpl' />
<aop:aspectj-autoproxy>
<aop:include name='stateAspectImpl' />
 
</aop:aspectj-autoproxy>
 
</beans>

The disadvantage of this solution is: each time you have to create a new advice, you have to remember change the xml file to declare the advice and  add the advice to the proxy.

——————————————–

References:

Spring AOP Reference Documentation.

Article in Java Geek Codes: a quick tutorial with full code.

http://stackoverflow.com/questions/3310115/spring-aop-advice-on-annotated-controllers

http://stackoverflow.com/questions/789759/how-can-i-apply-an-aspect-using-annotations-in-spring

http://stackoverflow.com/questions/9310927/aspect-not-executed-in-spring

http://stackoverflow.com/questions/3991249/how-can-i-combine-aspect-with-controller-in-spring-3

http://stackoverflow.com/questions/1483063/spring-mvc-3-and-handling-static-content-am-i-missing-something

 

Tags: , ,

Category: Development, Java

Comments (2)

Trackback URL | Comments RSS Feed

  1. WangQi says:

    It seems that you must define the following code in serverlet.xml.

  2. William Cornejo says:

    Excellent retrieval of sources regarding Spring Controller’s AOP weaving. Thanks for sharing!

Leave a Reply