Spring AOP 自定義切面不生效


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/>
spring-default.xml 配置啟用AspectJ
@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};
}
自定義注解IsAdmin
@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);
    //}
}
聲明切面-處理注解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");
    }
}
TestController
<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>
spring-dispatcher.xml
<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>
web.xml

 

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.總結


  1. 盡量使用一個ApplicationWebContext, 或者在子上下文類(spring-dispatcher.xml)中配置AOP。
  2. 另外待整理CASE,@Transactional 注解未生效?

參考鏈接:

SpringMVC+AOP代理

SpringAOP-梳理

SpringMVC-梳理

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM