记录请求的耗时(拦截器、过滤器、aspect)
生活随笔
收集整理的這篇文章主要介紹了
记录请求的耗时(拦截器、过滤器、aspect)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
記錄請求的耗時(攔截器、過濾器、aspect)
文章前言
記錄控制器請求的耗時處理通常有三種實現方式,分別是:過濾器、攔截器、aspect;下文將逐一實現。
?
1、Filter 過濾器
1.1、方法說明
需要實現 Filter?類,主要涉及三個方法:1.2、代碼部分
@Component //表明作為spring的一個bean public class TimeFilter implements Filter {@Overridepublic void destroy() {System.out.println("time filter destroy");}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("time filter start");long start = new Date().getTime();//過濾器主要邏輯,整個處理流程 chain.doFilter(request, response);System.out.println("time filter 耗時:"+ (new Date().getTime() - start));System.out.println("time filter finish");}@Overridepublic void init(FilterConfig arg0) throws ServletException {System.out.println("time filter init");}}1.3、補充說明
通過過濾器攔截請求的方式,有一個問題,只能拿到 http?的請求和響應(request、response),意味著只能在請求或者響應里獲取一些參數; 但是當前請求到底是哪個控制器的哪個方法處理的,在filter里是無法獲取的,因為實現的 Filter 這個接口是由javax(j2e)定義的, 而我們要攔截的控制器(Controller)中的請求是springmvc定義的,所以 Filter 是無法獲取 spring 相關信息。? 帶出下一個主角,Interceptor 攔截器,springmvc 提供。2、Interceptor 攔截器
2.1、方法說明
需要實現 HandlerInterceptor 類,主要涉及三個方法:2.2、代碼部分
@Component public class TimeInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {System.out.println("preHandle");System.out.println(((HandlerMethod)handler).getBean().getClass().getName());System.out.println(((HandlerMethod)handler).getMethod().getName());request.setAttribute("startTime", new Date().getTime());return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {System.out.println("postHandle");Long start = (Long) request.getAttribute("startTime");System.out.println("time interceptor 耗時:"+ (new Date().getTime() - start));}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {System.out.println("afterCompletion");Long start = (Long) request.getAttribute("startTime");System.out.println("time interceptor 耗時:"+ (new Date().getTime() - start));System.out.println("ex is "+ex);}}2.3、補充部分
interceptor 其實等價于將 filter? doFilter()?方法中的?chain.doFilter(request, response) 分成兩部分:preHandle() 、?postHandle()? interceptor比?filter?的優勢在于方法上多了另外一個參數,Object handler?該參數是用來處理當前 request?請求的控制器方法的聲明; 意味著,你可以通過該參數獲取當前控制器相關的信息,如控制器名稱、請求的方法名。2.4、注意部分
interceptor 跟 Filter?不一樣,光聲明一個 @Component?是無法達到攔截器起作用,還需要一些額外的配置。 @Configuration public class WebConfig extends WebMvcConfigurerAdapter {@SuppressWarnings("unused")@Autowiredprivate TimeInterceptor timeInterceptor;// 攔截器的一個注冊器 @Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(timeInterceptor);}} 這個配置類需要繼承 WebMvcConfigurerAdapter,并重寫添加攔截器的方法 addInterceptors,將自定義攔截器添加到應用中。 這時候攔截器就生效了。?2.5、繼續補充
攔截器會攔截所有控制器里的方法調用。但是卻有一個缺陷,其無法獲取前端訪問方法的時候所攜帶的參數的。 為什么會這么說? 從Spring MVC 的 DispatcherServlet 的源代碼中可以發現,找到 doDispatch() 方法,也就是請求分發的方法,有一段代碼如下: 如果我們自定的 Interceptor 的 preHandler 方法返回的是 false,分發任務就會截止,不再繼續執行下面的代碼, 而下面的一行代碼正是將前端攜帶的參數進行映射的邏輯,也就是說,preHandler 方法不會接觸到前端攜帶來的參數,也就是說攔截器無法處理參數。 所以這里引進 AOP 進行攔截。3、Aspect
描述AOP常用的一些術語有:通知(Adivce)、連接點(Join point)、切點(Pointcut)、切面(Aspect)、引入(Introduction)、織入(Weaving) 首先明確的核心概念:切面 = 切點 + 通知。3.1、通知(Adivce)
通知分為五種類型: Before(前置通知):在目標方法被調用之前調用通知功能 After(后置通知):在目標方法完成后調用通知,無論方法是否執行成功,不會關心方法的輸出是什么 After-returning(返回通知):在目標方法成功執行之后調用通知 After-throwing(異常通知):在目標方法拋出異常后調用通知 Around(通知環繞):通知包裹了被通知的方法,在被通知的方法調用之前和調用之后執行自定義的行為3.2、連接點(Join point)
連接點是一個應用執行過程中能夠插入一個切面的點。 比如:方法調用、方法執行、字段設置/獲取、異常處理執行、類初始化、甚至是for循環中的某個點。 理論上, 程序執行過程中的任何時點都可以作為作為織入點, 而所有這些執行時點都是Joint point, 但 Spring AOP 目前僅支持方法執行 (method execution)。3.3、切點(Pointcut)
通知(advice)定義了切面何時,那么切點就是定義切面“何處” 描述某一類 Joint points, 比如定義了很多 Joint point, 對于 Spring AOP 來說就是匹配哪些方法的執行。3.4、切面(Aspect)
切面是切點和通知的結合。通知和切點共同定義了關于切面的全部內容 —— 它是什么時候,在何時和何處完成功能。3.5、引入(Introduction)
引用允許我們向現有的類添加新的方法或者屬性3.6、織入(Weaving)
組裝方面來創建一個被通知對象。這可以在編譯時完成(例如使用AspectJ編譯器),也可以在運行時完成。 Spring和其他純Java AOP框架一樣,在運行時完成織入。?
?
來看一下 Aspect 怎么寫: @Aspect @Component public class TimeAspect {@Around("execution(* club.sscai.security.web.controller.UserController.*(..))")public Object handleTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println("time aspect is start.");for (Object object : proceedingJoinPoint.getArgs()) {System.out.println(object);}long startTime = System.currentTimeMillis();Object obj = proceedingJoinPoint.proceed();System.out.println("time aspect 耗時:" + (System.currentTimeMillis() - startTime));System.out.println("time aspect finish.");return obj;} } @Around 定義了環繞通知,也就是定義了何時使用切面,表達式"execution(* club.sscai.security.web.controller.UserController.*(..))"定義了再哪里使用。 ProceedingJoinPoint 對象的 proceed() 方法表示執行被攔截的方法,它有一個 Object 類型的返回值,是原有方法的返回值,后期使用的時候往往需要強轉。 對于上面三種攔截方式,他們的執行有一個基本的順序,進入的順序是: Filter-->Interceptor-->Aspect-->Controller-->Aspect-->Interceptor-->Filter(不考慮異常的發生)。 如下所示:?
博客地址:http://www.cnblogs.com/niceyoo posted @ 2018-12-22 19:02 niceyoo 閱讀(...) 評論(...) 編輯 收藏總結
以上是生活随笔為你收集整理的记录请求的耗时(拦截器、过滤器、aspect)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python--14 递归
- 下一篇: Didn‘t find class “a