• spring boot:多个filter/多个interceptor/多个aop时设置调用的先后顺序(spring boot 2.3.1)


    一,filter/interceptor/aop生效的先后顺序?

    1,filter即过滤器,基于servlet容器,处于最外层,

        所以它会最先起作用,最后才停止

      说明:filter对所有访问到servlet容器的url都有效,包括静态资源

    2,interceptor即拦截器,基于web框架,它会在filter之后起作用

      说明:spring boot 1.x中,静态资源已被interceptor排除,

              spring boot 2.x中,需要自己手动排除到静态资源的访问

      filter和interceptor都是作用于请求

    3,aop即切面,基于Spring的IOC容器,对spring管理的bean有效,

             它会在interceptor之后才生效

             aop可以作用于类和方法

    如图:

    说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest

             对应的源码可以访问这里获取: https://github.com/liuhongdi/

    说明:作者:刘宏缔 邮箱: 371125307@qq.com

    二,演示项目的相关信息

    1,项目地址:

    https://github.com/liuhongdi/costtime

    2,项目的原理:

       项目中使用了两个filter,两个interceptor,两个aspect,

        功能分别都是:计算请求或方法执行的时间,   打印请求的参数

        然后观察它们被执行到的顺序

    3,项目结构:如图:

    三,配置文件说明 :

    pom.xml

            <!--aop begin-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>

    用来引入aop

    四,java代码说明

    1,DefaultMvcConfig.java

    @Configuration
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    public class DefaultMvcConfig implements WebMvcConfigurer {
        @Resource
        private LogInterceptor logInterceptor;
        @Resource
        private CostTimeInterceptor costTimeInterceptor;
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("/home/home");
        }
    
        //添加Interceptor
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //1.加入的顺序就是拦截器执行的顺序,
            //2.按顺序执行所有拦截器的preHandle
            //3.所有的preHandle 执行完再执行全部postHandle 最后是postHandle
            registry.addInterceptor(costTimeInterceptor)
                    .addPathPatterns("/home/home**")
                    .excludePathPatterns("/html/*","/js/*");
            registry.addInterceptor(logInterceptor)
                    .addPathPatterns("/**")
    .excludePathPatterns("/html/*","/static/**","/images/**");
        }
    
        //add filter
        @Bean
        public FilterRegistrationBean addTimeFilterBean() {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            registration.setFilter(new TimeFilter());
            registration.setName("timeFilter");
            registration.setOrder(2);  //请求中过滤器执行的先后顺序,值越小越先执行
            registration.addUrlPatterns("/home/*","/abc/*");
            return registration;
        }
    
        @Bean
        public FilterRegistrationBean addLogFilterBean() {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            registration.setFilter(new LogFilter());
            registration.setName("logFilter");
            registration.setOrder(1);  //请求中过滤器执行的先后顺序,值越小越先执行
            registration.addUrlPatterns("/*");
            registration.addInitParameter("exclusions","/js/*,/images/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid2/*");
            return registration;
        }
    }

    说明:拦截器是按加入到registry的顺序执行

    filter是按setOrder中指定的顺序执行

    另外:filter也可以用Order注解来指定顺序

    2,CostTimeAspect.java

    @Component
    @Aspect
    @Order(3)
    public class CostTimeAspect {
        ThreadLocal<Long> startTime = new ThreadLocal<>();
        @Pointcut("execution(public * com.costtime.demo.controller.*.*(..))")
        private void pointcut() {}
    
       //用around得到方法使用的时间
        @Around(value = "pointcut()")
        public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("------costtime aop around begin");
            long begin = System.nanoTime();
            Object obj=joinPoint.proceed();
            long end =System.nanoTime();
            long timeMicro = (end-begin)/1000;
            System.out.println("costtime aop 方法around:微秒数:"+timeMicro);
            System.out.println("------costtime aop around end");
            return obj;
        }
    
        @Before("pointcut()")
        public void doBefore(JoinPoint joinPoint) throws Throwable{
            System.out.println("------costtime aop doBefore begin");
            startTime.set(System.currentTimeMillis());
        }
    
        //和doBefore搭配,得到使用的时间
        @AfterReturning(returning = "ret" , pointcut = "pointcut()")
        public void doAfterReturning(Object ret){
            System.out.println("------costtime aop doAfterReturning begin");
            System.out.println("costtime aop 方法doafterreturning:毫秒数:"+ (System.currentTimeMillis() - startTime.get()));
        }
    }

    3,LogAspect.java

    @Component
    @Aspect
    @Order(1)
    public class LogAspect {
        @Pointcut("execution(public * com.costtime.demo.controller.*.*(..))")
        private void pointcut() {}
    
        @Around(value = "pointcut()")
        public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("------log aop around begin");
            Object obj=joinPoint.proceed();
            System.out.println("------log aop around end");
            return obj;
        }
    
        //把请求参数打印出来
        @Before("pointcut()")
        public void doBefore(JoinPoint joinPoint) throws Throwable{
            System.out.println("------log aop doBefore begin");
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
    
            //得到方法的参数名和参数值
            Signature signature = joinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature)signature;
            String[] paramNames = methodSignature.getParameterNames();
            Object[] args = joinPoint.getArgs();
            String paramValue = "";
            Map<String, Object> nameAndArgs = new HashMap<String, Object>();
            for (int i = 0; i < paramNames.length; i++) {
                paramValue+="parameter:"+paramNames[i]+";value:"+args[i];
            }
    
            // 记录下请求内容
            System.out.println("log aop URL : " + request.getRequestURL().toString());
            System.out.println("log aop PARAM : " +  request.getQueryString());
            System.out.println("log aop HTTP_METHOD : " + request.getMethod());
            System.out.println("log aop IP : " + request.getRemoteAddr());
            System.out.println("log aop METHOD CLASS : " + joinPoint.getSignature().getDeclaringTypeName() );
            System.out.println("log aop METHOD NAME: " + joinPoint.getSignature().getName());
            System.out.println("log aop METHOD ARGS : " + paramValue);
        }
    
        @AfterReturning(returning = "ret" , pointcut = "pointcut()")
        public void doAfterReturning(Object ret){
            System.out.println("------log aop doAfterReturning begin");
        }
    }

    说明:这两个aspect用Order注解来指定执行的先后顺序,

            值越小执行顺序越靠前

    4,过滤器和拦截器的代码因为功能基本一致,为节省篇幅,不再贴出,

       大家可以从github上访问:

    https://github.com/liuhongdi/costtime

    五,测试启动顺序的效果

    1,访问url:

    http://127.0.0.1:8080/home/home?v=1

    查看控制台的输出:

    ----------------log filter doFilter begin
    ===执行过滤器功能
    log filter URL : http://127.0.0.1:8080/home/home
    log filter PARAM : v=1
    log filter HTTP_METHOD : GET
    log filter IP : 127.0.0.1
    ----------------time filter doFilter begin---------------time interceptor preHandle 
    [interceptor] request parameters: name:v;value:1
    ---------------log interceptor preHandle 
    [interceptor] request parameters: name:v;value:1------log aop around begin
    ------log aop doBefore begin
    log aop URL : http://127.0.0.1:8080/home/home
    log aop PARAM : v=1
    log aop HTTP_METHOD : GET
    log aop IP : 127.0.0.1
    log aop METHOD CLASS : com.costtime.demo.controller.HomeController
    log aop METHOD NAME: homeMethod
    log aop METHOD ARGS : parameter:version;value:1
    ------costtime aop around begin
    ------costtime aop doBefore begin
    ------costtime aop doAfterReturning begin
    costtime aop 方法doafterreturning:毫秒数:1027
    costtime aop 方法around:微秒数:1027548
    ------costtime aop around end
    ------log aop doAfterReturning begin
    ------log aop around end
    ---------------log interceptor postHandle 
    ---------------time interceptor postHandle 
    time interceptor 方法 postHandle:毫秒数:1108
    ---------------log interceptor afterCompletion 
    ---------------time interceptor afterCompletion 
    timefilter: /home/home costtime: 1128ms 
    ----------------time filter doFilter end
    ----------------log filter doFilter end

    2,可以看到:

       大类的启动顺序是:

       filter

       interceptor

       aop

    3,可以看到

         filter的启动顺序,是按我们在config中设定的order顺序

         interceptor的启动顺序,是addInterceptor到registry的顺序

         同一个interceptor内部:执行顺序是: preHandle,postHandle,afterCompletion

         aop的启动顺序:是我们在Order注解中指定的顺序

         同一个aop内部:around比dobefore启动更早

    六,查看spring boot的版本

      .   ____          _            __ _ _
     /\ / ___'_ __ _ _(_)_ __  __ _    
    ( ( )\___ | '_ | '_| | '_ / _` |    
     \/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.3.1.RELEASE)
  • 相关阅读:
    [Leetcode][Python]29: Divide Two Integers
    [Leetcode][Python]28: Implement strStr()
    [Leetcode][Python]27: Remove Element
    前端笔记(百度)
    关于html5新增的功能(百度)
    一些我容易混淆的知识(关于数组的操作、对字符串的操作)
    web前端~~浏览器兼容问题(百度)
    关于前端的性能优化问题
    封装ajax(二)闭包的形式
    封装ajax
  • 原文地址:https://www.cnblogs.com/architectforest/p/13344376.html
Copyright © 2020-2023  润新知