思路:實現思路都是基於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);
}
}
