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