思路:实现思路都是基于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); } }