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)
原文地址:https://www.cnblogs.com/architectforest/p/13344376.html