spring boot通過Interceptor和HandlerMethodReturnValueHandler實現統一處理為controller返回對象統計處理時間


思路:實現思路都是基於Aop實現,方式上可以通過spring aop和spring mvc的aop機制都能實現。

通過Interceptor的可以實現為controller插入開始時間和執行結束時間,並將數據放入response中,但是這里希望將數據直接放入ResponseBody Controller返回的統一對象中,所以用Interceptor有點麻煩,可以使用spring mvc的HandlerMethodReturnValueHandler對結果對象進行處理,然后在交給RequestResponseBodyMethodProcessor處理。

1、通過Interceptor在進入controller前將時間存入request中;

2、在HandlerMethodReturnValueHandler攔截並統計時間,將耗時放入對象中。

采坑記錄:

1、在WebMvcConfigurerAdapter重寫addReturnValueHandlers方法,發現supportsReturnType不會被執行,原因:@ResponseBody或@RestController的實現也是由HandlerMethodReturnValueHandler實現,因為系統級別高於用戶自定義級別,會先執行RequestResponseBodyMethodProcessor的supportsReturnType,當返回true后,后續的久不會再執行,只會有1個HandlerMethodReturnValueHandler會被執行,解決思路是在WebMvcConfigurerAdapter中改變自定義的順序,插入到系統之前(List)。

2、自定義的ReturnHandler執行后系統的將不會執行,將導致需要自己實現MessageConvert,即如果不自己實現response將沒有任何返回數據,解決思路是繼續使用@ResponseBody或@RestController前提下在自定義的ReturnHandler中調用RequestResponseBodyMethodProcessor的實現進行處理,RequestResponseBodyMethodProcessor需要一個構造參數:List<HttpMessageConverter<?>> converters,可以注入RequestMappingHandlerAdapter進行獲取。

代碼如下:

1、WebFileConfigurer

@Configuration
public class WebFileConfigurer extends WebMvcConfigurerAdapter {

    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    /**
     * 注冊SpringMVC Interceptor
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注冊攔截器
        // 為每個請求統計處理耗時
        InterceptorRegistration ir = registry.addInterceptor(new ProcessInterceptor());
    }

    @Bean
    public ReturnHandler getReturnHandler(){
        List<HttpMessageConverter<?>> messageConverters = requestMappingHandlerAdapter.getMessageConverters();
        return new ReturnHandler(messageConverters);//初始化過濾器
    }


    /**
     * 解決ReturnHandler不生效問題
     */
    @PostConstruct
    public void init() {
        final List<HandlerMethodReturnValueHandler> originalHandlers = new ArrayList<>(
                requestMappingHandlerAdapter.getReturnValueHandlers());
        //    final int deferredPos = obtainValueHandlerPosition(originalHandlers, DeferredResultMethodReturnValueHandler.class);
        //    originalHandlers.add(deferredPos + 1, customedReturnValueHandler());

        // Add customed handler BEFORE the HttpEntityMethodProcessor to enable JsonReturnHandler !!
        final int deferredPos = obtainValueHandlerPosition(originalHandlers, HttpEntityMethodProcessor.class);
        originalHandlers.add(deferredPos - 1, getReturnHandler());
        requestMappingHandlerAdapter.setReturnValueHandlers(originalHandlers);
        return;
    }

    private int obtainValueHandlerPosition(final List<HandlerMethodReturnValueHandler> originalHandlers, Class<?> handlerClass) {
        for (int i = 0; i < originalHandlers.size(); i++) {
            final HandlerMethodReturnValueHandler valueHandler = originalHandlers.get(i);
            if (handlerClass.isAssignableFrom(valueHandler.getClass())) {
                return i;
            }
        }
        return -1;
    }

}

2、Interceptor

public class ProcessInterceptor extends HandlerInterceptorAdapter {
    private final static Logger logger = getLogger(ProcessInterceptor.class);

    public final static  String requestName="requestStartTime";

    /**
     *預處理回調方法,實現處理器的預處理(如登錄檢查)。
     *第三個參數為響應的處理器,即controller。
     *返回true,表示繼續流程,調用下一個攔截器或者處理器。
     *返回false,表示流程中斷,通過response產生響應。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        //記錄開始時間
        Long requestStartTime = System.currentTimeMillis();
        request.setAttribute(requestName,requestStartTime);
        return true;
    }

}

3、ReturnHandler

@Component
public class ReturnHandler extends RequestResponseBodyMethodProcessor {

    private final static Logger logger = getLogger(ReturnHandler.class);


    public ReturnHandler(List<HttpMessageConverter<?>> converters) {
        super(converters);
    }



        /**
         * 該處理程序是否支持給定的方法返回類型(只有返回true才回去調用handleReturnValue)
         */
        @Override
        public boolean supportsReturnType(MethodParameter methodParameter) {
            boolean support = super.supportsReturnType(methodParameter);
            support=support || methodParameter.getParameterType() == WebResponse.class;
            return support;
        }

        /**
         * 處理給定的返回值
         * 通過向 ModelAndViewContainer 添加屬性和設置視圖或者
         * 通過調用 ModelAndViewContainer.setRequestHandled(true) 來標識響應已經被直接處理(不再調用視圖解析器)
         */
        @Override
        public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) throws IOException, HttpMediaTypeNotAcceptableException {

            if ( o instanceof  WebResponse){
                WebResponse res=((WebResponse)o);
                /**
                 * 標識請求是否已經在該方法內完成處理
                 */
                modelAndViewContainer.setRequestHandled(true);
                HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
                HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
                //獲取開始時間
                Object startTimerObj = request.getAttribute(ProcessInterceptor.requestName);
                if (startTimerObj==null || !(startTimerObj instanceof Long) || res==null){
                    //沒有開始時間,不做處理的請求
                    return;
                }
                Long startTimer=(Long) startTimerObj;
                Long millis=System.currentTimeMillis()-startTimer;
                //記錄處理時間
                res.setT(millis);

                logger.debug(String.format("請求url:%s 耗時:%s",request.getRequestURL().toString(),millis.toString()));

            }
            super.handleReturnValue(o,methodParameter,modelAndViewContainer,nativeWebRequest);
        }


}

  

 


免責聲明!

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



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