Spring mvc controller AOP 失效问题

本文解决了一个常见问题:如何在 Spring MVC 控制器中使用 Spring AOP 的注解方式来实现方法拦截。通过调整配置文件或使用特定的 XML 文件来确保控制器处于 DispatcherServlet 的扫描范围内。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 http://stackoverflow.com/questions/17834958/spring-aop-is-not-working-in-with-mvc-structure?rq=1 

Spring AOP Advice on Annotated Controllers

Paco |  April 2, 2013 1 Comment

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: aopfeaturedjavaspring

CategoryDevelopmentJava

<think>嗯,用户现在想了解SSM框架(SpringSpring MVC、MyBatis)的集成与使用教程。之前他们问过SSM框架与JavaSE、JavaWeb的关系和使用场景,现在需要更具体的集成步骤和使用方法。我需要先回忆之前的回答内容,确保这次教程和之前的讨论连贯。首先,用户可能需要一个从零开始搭建SSM项目的步骤。之前的回答提到了SSM如何优化JavaWeb开发,所以现在需要详细说明如何整合这三个框架。我应该分步骤讲解,包括环境准备、配置文件和代码示例。接下来,用户可能关心每个框架在集成中的作用。比如Spring的IoC和AOPSpring MVC处理Web层,MyBatis负责数据持久化。需要明确各层的配置,比如Spring的applicationContext.xml,SpringMVC的DispatcherServlet配置,以及MyBatis的SqlSessionFactory和Mapper扫描。 还要注意配置文件之间的依赖关系,比如如何在Spring中整合MyBatis,需要配置数据源、事务管理等。同时,要给出具体的代码示例,比如Controller、Service、Mapper接口和XML文件,这样用户能更直观地理解。 另外,用户可能遇到常见的集成问题,比如配置错误、依赖冲突或者事务管理问题。需要提前指出这些可能的坑,并提供解决方法,比如检查XML配置、使用Maven管理依赖等。最后,生成的相关问题应该围绕集成中的难点和应用场景,比如事务管理、RESTfulAPI开发、性能优化等,帮助用户进一步深入学习。确保回答结构清晰,分步骤讲解,代码示例正确,并引用之前的参考资料。同时,按照用户的要求,使用LaTeX格式的数学表达式(如果有的话),但在这个主题下可能用不上,所以重点放在代码和配置示例上。</think>### SSM框架集成与使用教程(Spring + Spring MVC + MyBatis) --- #### 一、环境准备 1. **基础依赖**: - JDK 1.8+ - Maven 3.6+ - Tomcat 9+ - MySQL 5.7+ - IDE(推荐IntelliJ IDEA或Eclipse) 2. **Maven依赖**(`pom.xml`核心配置): ```xml <!-- Spring 核心 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.10</version> </dependency> <!-- Spring MVC --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.10</version> </dependency> <!-- MyBatis 整合Spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.7</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> ``` --- #### 二、配置文件整合 ##### 1. **Spring核心配置**(`applicationContext.xml`) ```xml <!-- 数据源配置 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test_db?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </bean> <!-- MyBatis整合Spring --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*.xml"/> </bean> <!-- 扫描Mapper接口 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.example.dao"/> </bean> <!-- 事务管理 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> ``` ##### 2. **Spring MVC配置**(`spring-mvc.xml`) ```xml <!-- 开启注解驱动 --> <mvc:annotation-driven/> <!-- 视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 扫描Controller --> <context:component-scan base-package="com.example.controller"/> ``` ##### 3. **Web.xml配置**(入口配置) ```xml <!-- Spring容器初始化 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Spring MVC DispatcherServlet --> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> ``` --- #### 三、代码开发示例 ##### 1. **实体类**(POJO) ```java public class User { private Integer id; private String name; // Getter/Setter省略 } ``` ##### 2. **Mapper接口与XML映射** ```java // UserMapper.java public interface UserMapper { @Select("SELECT * FROM user WHERE id = #{id}") User selectUserById(int id); } ``` ##### 3. **Service层** ```java @Service public class UserService { @Autowired private UserMapper userMapper; public User getUserById(int id) { return userMapper.selectUserById(id); } } ``` ##### 4. **Controller层** ```java @Controller public class UserController { @Autowired private UserService userService; @RequestMapping("/user/{id}") public String getUser(@PathVariable int id, Model model) { model.addAttribute("user", userService.getUserById(id)); return "userDetail"; } } ``` --- #### 四、常见问题与调试 1. **依赖冲突**: - 使用`mvn dependency:tree`检查依赖版本冲突。 - 例如:Spring 5.x需要Jackson 2.9+,低版本会导致JSON解析失败[^1]。 2. **事务失效**: - 确保在Service方法上添加`@Transactional`注解。 - 检查`applicationContext.xml`中是否启用了事务管理器[^2]。 3. **MyBatis映射错误**: - 检查XML映射文件路径是否与`sqlSessionFactory`配置一致。 - 确认SQL语句中的字段名与数据库表结构匹配[^3]。 --- ### 相关问题 1. **如何在SSM框架中实现分布式事务管理?**[^2] 2. **Spring MVC如何支持RESTful API开发?**[^1] 3. **MyBatis动态SQL有哪些常用标签?**[^3] 4. **如何优化SSM项目的启动速度?**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值