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);
        }


}

  

原文地址:https://www.cnblogs.com/jifeng/p/9722096.html