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