spring mvc 处理Controller返回结果
我们总有这样的需求,例如想动态的处理controller(或是一部分)的返回结果,增加某些字段,或是格式化返回结果。这就让我们想到了拦截器HandlerInterceptorAdapter和AOPAspect。当时使用AOPAspect可以实现,并且方便简单。网络上一大堆的使用方式,在此不做详细解释了。下面我们来说使用MVC拦截器的方式。
- ModelAndView 返回
可以使用postHandle在postHandle中处理model。就可以实现对返回值动态修改。
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
Map<String, Object> model = modelAndView.getModel();
}
以下这两篇文章
例子1
例子2
对于拦截器中返回值的介绍已经够充分,下面主要说下有问题的地方,对于HandlerMethodReturnValueHandler的使用,主要体现在DispatcherServlet.doDispatch之后一直会调用到 requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);//这就是返回值
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
go on …
HandlerMethodReturnValueHandlerComposite.getReturnValueHandler
/**
* Find a registered {@link HandlerMethodReturnValueHandler} that supports the given return type.
*/
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" +
returnType.getGenericParameterType() + "]");
}
if (returnValueHandler.supportsReturnType(returnType)) {
return returnValueHandler; //找到后立即返回,并且只返回一个
}
}
return null;
}
returnValueHandlers会包含多个,如下。
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
handlers.add(new StreamingResponseBodyReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
handlers.add(new ListenableFutureReturnValueHandler());
if (completionStagePresent) {
handlers.add(new CompletionStageReturnValueHandler());
}
// Annotation-based return value types
handlers.add(new ModelAttributeMethodProcessor(false));
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());
// 自己添加的HandlerMethodReturnValueHandler类型
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
所以及时我们 returnValueHandlers.add(JsonReturnHandler()); 之后 我们自定义的HandlerMethodReturnValueHandler类型会被放置到大概 13的位置,对自定义返回的Controller 来说到第10个RequestResponseBodyMethodProcessor的时候就会被处理了,所以一直不会被我们自定义的ReturnValueHandler处理,那么怎么办呢?直接上我改好的
在 项目自己的 WebAppConfig( WebMvcConfigurerAdapter) 中
@Bean
public MtopHandlerMethodReturnValueHandler getMtopHandlerMethodReturnValueHandler(){
MtopHandlerMethodReturnValueHandler methodReturnValueHandler = new MtopHandlerMethodReturnValueHandler();
return methodReturnValueHandler;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
RequestMappingHandlerAdapter requestMappingHandlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
returnValueHandlers.add(getMtopHandlerMethodReturnValueHandler());
returnValueHandlers.addAll(requestMappingHandlerAdapter.getReturnValueHandlers());
requestMappingHandlerAdapter.setReturnValueHandlers(returnValueHandlers);
}
然后注意自定义的HandlerMethodReturnValueHandler的handleReturnValue 中 加入 mavContainer.setRequestHandled(true);这样我们就把HandlerMethodReturnValueHandler放到了最前面,直接就返回了,有同学要问了,那是不是这样就修改了returnValueHandlers内部的顺序会不会对源程序有影响?嗯 的确会的。所以我们可以在配置注解的方式(也就是上面的例子中supportsReturnType使用的方式),表明下只有加了注解的才进入自定义的HandlerMethodReturnValueHandler中就完美了。
以上内容都是我debug和分析源码找到的,并且已经实现。