Spring AOP 對Spring MVC的Controller切面攔截不起作用

猿碼道發表於2018-02-02

1 問題描述

當使用Spring AOP對Controller層的Controller類的方法進行切面攔截,不起作用。AOP配置沒有任何問題。

2 排查過程

  1. Spring AOP配置沒有任何問題;【正常】
  2. 斷點除錯:Spring原始碼斷點除錯,在呼叫Controller方法時,Controller的例項被JDK進行動態代理了;【不正常】
  3. Spring預設的代理方式為JDK動態代理;【正常】

3 解決問題

AOP有的人說攔截不到Controller。有的人說想攔AnnotationMethodHandlerAdapter截到Controller必須得攔截org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

首先AOP可以攔截到Controller的,這個是毋容置疑的其次須攔截AnnotationMethodHandlerAdapter也不是必須的。最起碼我沒有驗證成功過這個。這個方式就不在這兒介紹說明了。

AOP之所以有的人說攔截不到Controller, 原因是該註解的Controller已被spring容器內部代理了。我們只要把它交給cglib代理就可以了。Spring MVC的配置檔案dispatcher-servlet.xml:

<!-- 通知spring使用cglib而不是jdk的來生成代理方法 AOP可以攔截到Controller -->
<aop:aspectj-autoproxy proxy-target-class="true" />
複製程式碼

4 問題總結

Spring MVC 和 Spring 整合的時候,SpringMVC的dispatcher-servlet.xml檔案中配置掃描包,不要包含 service的註解,Spring的applicationContext.xml檔案中配置掃描包時,不要包含controller的註解,如下所示:

Spring MVC dispatcher-servlet.xml:

<context:component-scan base-package="com.qding">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>
複製程式碼

Spring MVC啟動時的配置檔案,包含元件掃描、url對映以及設定freemarker引數,讓Spring不掃描帶有@Service註解的類。為什麼要這樣設定?因為springmvc.xml與applicationContext.xml不是同時載入,如果不進行這樣的設定,那麼,Spring就會將所有帶@Service註解的類都掃描到容器中,等到載入applicationContext.xml的時候,會因為容器已經存在Service類,使得cglib將不對Service進行代理,直接導致的結果就是在applicationContext 中的事務配置不起作用,發生異常時,無法對資料進行回滾。以上就是原因所在。

同樣的在Spring的applicationContext.xml配置如下:

<context:component-scan base-package="com.qding">           
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
複製程式碼

context:component-scan掃描指定的包中的類上的註解,常用的註解有:

@Controller 宣告Action元件
@Service    宣告Service元件    @Service("myMovieLister") 
@Repository 宣告Dao元件
@Component   泛指元件, 當不好歸類時. 
@RequestMapping("/menu")  請求對映
@Resource  用於注入,( j2ee提供的 ) 預設按名稱裝配,@Resource(name="beanName") 
@Autowired 用於注入,(srping提供的) 預設按型別裝配 
@Transactional( rollbackFor={Exception.class}) 事務管理
@ResponseBody
@Scope("prototype")   設定bean的作用域
複製程式碼

相關文章