首页 > 编程语言 >SpringBoot项目中对定义的多个Aspect类排序

SpringBoot项目中对定义的多个Aspect类排序

时间:2024-04-13 17:35:03浏览次数:26  
标签:5.3 SpringBoot spring 18 ExposeInvocationInterceptor Aspect aop 排序 public

代码示例

@Configuration
public class AspectConfig {

    @Aspect
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public static class LogAspect {

        @Pointcut("execution(public * com.imooc.spring.web..*.*(..))")
        public void webLog() {
        }

        @Before("webLog()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            System.out.println("LogAspect1 requestURI: " + request.getRequestURI());
        }
    }

    @Aspect
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE + 1)
    public static class LogAspect2 {

        @Pointcut("execution(public * com.imooc.spring.web..*.*(..))")
        public void webLog() {
        }

        @Before("webLog()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            System.out.println("LogAspect2 requestURI: " + request.getRequestURI());
        }
    }

    @Aspect
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE + 2)
    public static class LogAspect3 {

        @Pointcut("execution(public * com.imooc.spring.web..*.*(..))")
        public void webLog() {
        }

        @Before("webLog()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            System.out.println("LogAspect3 requestURI: " + request.getRequestURI());
        }
    }
}

定义3个切面Aspect类,通过 Order 注解来排序,值越小优先级越高。

测试controller如下

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/testStringParamTrim")
    public TestObjectInfo testStringParamTrim(@RequestParam String goodsId, String goodsName) {
        return new TestObjectInfo().setGoodsId(goodsId).setGoodsName(goodsName);
    }

    @Data
    @Accessors(chain = true)
    public static class TestObjectInfo {
        private String goodsId;
        private String goodsName;
    }
}

遇到的问题

项目启动没报错,在执行时报以下错误

java.lang.IllegalStateException: No MethodInvocation found: Check that an AOP invocation is in progress and that the ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() must be invoked from the same thread.
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.currentInvocation(ExposeInvocationInterceptor.java:74) ~[spring-aop-5.3.18.jar:5.3.18]
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.getJoinPointMatch(AbstractAspectJAdvice.java:658) ~[spring-aop-5.3.18.jar:5.3.18]
	at org.springframework.aop.aspectj.AspectJMethodBeforeAdvice.before(AspectJMethodBeforeAdvice.java:44) ~[spring-aop-5.3.18.jar:5.3.18]
	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:57) ~[spring-aop-5.3.18.jar:5.3.18]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.18.jar:5.3.18]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.18.jar:5.3.18]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.18.jar:5.3.18]

原因分析

具体原因为,Spring 会自动帮我们添加一个 ExposeInvocationInterceptor 拦截器到我们的拦截器列表,它的优先级为 PriorityOrdered.HIGHEST_PRECEDENCE + 1,我们自己定义的 Aspect 优先级不能高于它。它的作用是将 MethodInvocation 保存到 ThreadLocal 中,供后续的拦截器使用,所以必须第一个执行。

添加的流程:

  1. AbstractAdvisorAutoProxyCreator 的 getAdvicesAndAdvisorsForBean() 方法来获取符合要求的拦截器。
  2. AspectJAwareAdvisorAutoProxyCreator 的 extendAdvisors() 方法对我们的拦截器列表进行了扩展,添加了 ExposeInvocationInterceptor 实例。
  3. 使用 AspectJPrecedenceComparator 对拦截器列表进行排序,底层使用 AnnotationAwareOrderComparator,支持 Order 注解和 Ordered 接口。

解决方法

不要设置优先级为 Ordered.HIGHEST_PRECEDENCE,最高优先级为 Ordered.HIGHEST_PRECEDENCE + 1

参考

Spring 源码阅读 70:容易被忽略的 ExposeInvocationInterceptor
Spring Aop 错误之:No MethodInvocation found ... the ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that advices with order HIGHEST

标签:5.3,SpringBoot,spring,18,ExposeInvocationInterceptor,Aspect,aop,排序,public
From: https://www.cnblogs.com/strongmore/p/18049571

相关文章

  • 排序算法-快速排序
    排序算法-快速排序一、快速排序介绍1.1原理介绍快速排序(QuickSort)是一种常用的排序算法,也是一种基于分治思想的排序算法。快速排序的基本思想是选取一个基准元素,将数组分成两部分,使得左边部分的元素都小于等于基准元素,右边部分的元素都大于等于基准元素,然后对左右两部分分别......
  • 排序算法
    排序算法1.排序算法定义:排序算法是一种将数据元素按特定顺序(通常是升序或降序)排列的算法。排序是计算机科学中最基本的操作之一,用于数据组织和优化搜索算法等。2、排序算法分类快速排序归并排序堆排序冒泡排序快速排序:快速排序是一种高效的分治排序算法,通过选定一个'......
  • P1155 [NOIP2008 提高组] 双栈排序
    P1155[NOIP2008提高组]双栈排序有思维的二分图染色题。对于“双”类的题目,我们通常分开考虑单个时的性质。对于一个栈,有一个基本的定理:若出现\(i<j<k\),有\(a_k<a_i<a_j\),那么一定不合法,即没有合法的出栈顺序使之有序。对于两个栈,我们相当于把序列分成两部分,使每部分之间......
  • SpringBoot中的Jetty使用及分析
    前言和Tomcat类似,Jetty也是一个Web应用服务器,相对于Tomcat,Jetty更加轻量、更加简易、更加灵活。今天通过代码来简单分析下SpringBoot中是如何启动Jetty的。Jetty简介使用importjava.io.File;importjava.io.IOException;importjava.net.InetAddress;import......
  • SpringBoot使用 nacos 会默认加载项目名配置文件
    问题描述boostrap.yml配置如下spring:application:name:cnblogscloud:nacos:config:server-addr:http://ip:8848namespace:d8b0df04-aa58-4a5b-b582-7d133b9e8b2c#命名空间IDfile-extension:yamlusern......
  • bufDataSet排序
    https://wiki.lazarus.freepascal.org/How_to_write_in-memory_database_applications_in_Lazarus/FPC SortingDBGridonTitleClickeventforTBufDataSetIfyouwishtoenableconsecutiveascendinganddescendingsortingofaDBGridshowingsomedatafromTBufD......
  • springboot集成redis
    首先引入依赖<!--redis坐标--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>在yml中引入redis数据库spring......
  • 常见的排序算法——冒泡排序(二)
    本文记述了冒泡排序微小改动的基本思想和一份参考实现代码,并在说明了算法的性能后用随机数据进行了验证。◆思想更少的比较可以节省一定的时间,此改动可以减少更小范围的比较。(把水平陈列的数组逆时针旋转90°后,有助于理解后续的内容。)将包含顶层以下的所有元素作为待排序范围......
  • 常见的排序算法——冒泡排序
    本文记述了冒泡排序的基本思想和一份参考实现代码,并在说明了算法的性能后用随机数据进行了验证。◆思想(把水平陈列的数组逆时针旋转90°后,有助于理解后续的内容。)将包含顶层以下的所有元素作为待排序范围,将该范围以上的所有元素作为已排序范围。通过一一比较相邻的两个元素,自......
  • Springboot2+vue2整合项目
    前端https://blog.csdn.net/m0_37613503/article/details/128961447数据库1.用户表CREATETABLE`x_user`(`id`int(11)NOTNULLAUTO_INCREMENT,`username`varchar(50)NOTNULL,`password`varchar(100)DEFAULTNULL,`email`varchar(50)DEFAULTNULL,`......