1.問題描述
自定義的IsAdminAspect切面,切入點為:所有被IsAdmin注解標記的方法。blog-service包下的可成功切入,對blog-api包下的Controller切入未生效。
項目目錄結構
|--blog
|--blog-api
|--src.main.java.com.blog.controller
|--TestController.java
|--src.main.webapp.WEB-INF
|--spring-dispatcher.xml
|--web.xml
|--blog-service
|--IsAdmin.java
|--IsAdminAspect.java
|--blog-conf
|--spring-default.xml
代碼如下:

<!-- 使AspectJ注解起作用:自動為匹配的類生產代理對象 --> <aop:aspectj-autoproxy/>

@Documented @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface IsAdmin { String message() default "當前用戶無管理員權限"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; /** * 允許訪問的權限列表 * 默認允許 SysUserRoleEnum.ADMIN 訪問 */ RoleEnum[] allowRole() default {RoleEnum.Admin}; }

@Slf4j @Aspect @Component public class IsAdminAspect extends BaseAspect { /** * 在方法執行前做驗證 * * @param joinPoint 切入點 * @param isAdmin isAdmin注解 */ @Before("@annotation(isAdmin)") public void doBefore(JoinPoint joinPoint, IsAdmin isAdmin) { log.info("IsAdminAspect check aspect,isAdmin={}", JsonUtil.toJson(isAdmin)); Object[] args = joinPoint.getArgs(); Assert.isTrue(ArrayUtils.isNotEmpty(args), "方法參數為空"); //校驗身份角色 checkUserRole(args, isAdmin.allowRole()); } ///** // * Class類上有IsAdmin注解,進行切入 // * // * @param joinPoint 切入點 // */ //@Before("@within(isAdmin)") //public void doBeforeSupportType(JoinPoint joinPoint, IsAdmin isAdmin) { // if (isAdmin == null) { // return; // } // log.info("doBeforeSupportType check isAdmin:{}", isAdmin); // doBefore(joinPoint, isAdmin); //} }

@Slf4j @Controller @RequestMapping("/blog/aop") @IsAdmin(allowRole = {RoleEnum.Super}) public class TestController { /** * 測試切面 */ @ResponseBody @RequestMapping(value = "/test", method = RequestMethod.POST) public void test() { log.info("haha"); } }

<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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.autonavi.collect.ggc.controller" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 訪問靜態資源文件, 不進controller --> <mvc:default-servlet-handler/> </beans>

<web-app> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/spring-*.xml</param-value> </context-param> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-dispatcher.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
2.排查思路
- AspectJ配置正確
-
- AspectJ 已配置啟用
- 配置所在位置:spring-default.xml
- 注解標注處,動態代理能夠生效。
- JDK動態代理:實現了接口+方法為public
- CGLIB: 方法為public/protected [JDK動態代理、CGLIB都不會攔截目標對象內的調用]
- Controller 沒有實現接口,應該會使用CGLIB動態代理。若controller實現了接口,需要顯式的配置class-based代理。
- 嘗試將配置改為<aop:aspectj-autoproxy proxy-target-class="true"/>,未解決
- 切入點表達式正確,詳細的切入點表達式參考DOC文檔
- 可能是由配置在spring-default.xml中導致,梳理下當前Spring Web應用的配置。
Spring Web應用的配置梳理
DispatcherServlet需要WebApplicationContext作為配置上下文,一般來說一個上下文就夠了。不過也有多個DispatcherServet實例,共享一個Root WebApplicationContext。
本文中的web.xml文件,即配置了多個上下文WebApplicationContext,繼承關系如下圖。spring-dispatcher為子上下文,spring-default為其父上下文。
分析后找到問題原因:AspectJ的配置啟用在父上下文中,未能對子上下文中的controllers生效
解決方案:spring-dispatcher.xml中增加啟用AspectJ的配置,並加載Bean IsAdminAspect
<!-- 解決 controller所有方法不生效的問題 --> <!-- 使AspectJ注解起作用:自動為匹配的類生產代理對象 --> <aop:aspectj-autoproxy/> <bean id="isAdminAspect" class="com.autonavi.collect.ggc.aspect.IsAdminAspect"/>
3.總結
- 盡量使用一個ApplicationWebContext, 或者在子上下文類(spring-dispatcher.xml)中配置AOP。
- 另外待整理CASE,@Transactional 注解未生效?
參考鏈接:
stackoverflow: https://stackoverflow.com/questions/3991249/how-can-i-combine-aspect-with-controller-in-spring-3