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

      

  • 相关阅读:
    tensorflow的日常Demo
    docker 搭建Mysql集群
    简单版nginx lua 完成定向流量分发策略
    简单版nginx lua 完成流量上报于中间件
    redis 数据备份持久化方案
    nginx 场景业务汇总 (中)
    nginx 场景业务汇总 (初)
    Sentinel 哨兵 实现redis高可用
    工地信息化——施工现场网格化管理系统实施小记
    小记 xian80 坐标转换 wgs84
  • 原文地址:https://www.cnblogs.com/jifeng/p/9722096.html
Copyright © 2020-2023  润新知