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)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM