由于某些时候需要对controller的返回对象作统一的封装,例如一个业务系统中统一的返回格式。这里可以使用到
ReturnValueHandler,当然也可以使用ResponseBody、Convertor或者View等。
编写类
ResultBeanReturnValueHandler实现接口HandlerMethodReturnValueHandler;将handler类注册到mvc中;
1.编写类ResultBeanReturnValueHandler实现接口HandlerMethodReturnValueHandler;
/**
* 结果封装类
*/
public class ResultBeanReturnValueHandler implements HandlerMethodReturnValueHandler {
/**
* 类似ResponseBody,仅有添加ResultBeanResponseBody注解的method才会触发
*/
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResultBeanResponseBody.class) != null ||
returnType.getMethodAnnotation(ResultBeanResponseBody.class) != null);
}
/**
* 使用统一的结果封装类ResultInfo,并序列化成json
*/
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
ResultInfo resultInfo = new ResultInfo<>(ResultInfo.OK, "success", returnValue);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
response.addHeader("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().append(JSON.toJSONString(resultInfo));
}
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceResultBeanResponseBody{
}
2.将handler类注册到mvc中;
可以使用mvc标签;
<mvc:annotation-driven>
<mvc:return-value-handlers>
<bean class="com.netease.vcloud.statics.dqs.system.returnhandler.ResultBeanReturnValueHandler" />
</mvc:return-value-handlers>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
也可以直接在RequestMappingHandlerAdapter中注入;
这里直接上源码看吧 <-- 太懒了
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
/**
* 这里可以看到只要 注册到customArgumentResolvers里就可以了
*/
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
/**
* Provide resolvers for custom argument types. Custom resolvers are ordered
* after built-in ones. To override the built-in support for argument
* resolution use {@link #setArgumentResolvers} instead.
*/
public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
this.customArgumentResolvers = argumentResolvers;
}
/**
* Return the custom argument resolvers, or {@code null}.
*/
public List<HandlerMethodArgumentResolver> getCustomArgumentResolvers() {
return this.customArgumentResolvers;
}
/**
* Return the list of return value handlers to use including built-in and
* custom handlers provided via {@link #setReturnValueHandlers}.
*/
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());
// Custom return value types
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
}
else {
handlers.add(new ModelAttributeMethodProcessor(true));
}
return handlers;
}
}
附赠一个使用过程中的小问题:
@Controller
@RequestMapping("/live/block")
public class LiveBlockController {
@RequestMapping("/getCdnBlockRateByAreaAndIsp")
public @ResultBeanResponseBody String getCdnBlockRateByAreaAndIsp(
@RequestParam("countory") String countory,
@RequestParam("province") String province,
@RequestParam("city") String city,
@RequestParam("isp") String isp){
return "123123";
}
}
这里本想可以统一进行处理,但是一直执行不到我的
ResultBeanReturnValueHandler类中,于是直接翻看
RequestMappingHandlerAdapter类的
源码,找到
invokeHandlerMethod函数
。
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* @since 4.2
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
大致过下前情,一个请求进来时,DispatcherServlet会找到其匹配的HandlerMapping和HandlerAdapter,然后会执行
HandlerAdapter的
handle方法->
handleInternal方法->
invokeHandlerMethod方法。
最后追到
invocableMethod.invokeAndHandle ->
returnValueHandlers
.handleReturnValue函数;
整个过程总结就是对请求进行封装和前置处理,然后执行对应的controller.method方法,最后对结果进行封装。
追踪源码的顺序是
HandlerAdapter.
handle ->
handleInternal ->
invokeHandlerMethod ->
invocableMethod.invokeAndHandle ->
returnValueHandlers
.handleReturnValue <-- 这就是目标函数了
函数
handleReturnValue下会进行结果处理类
ReturnValueHandler
的选择和执行。
/**
* Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it.
* @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
*/
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
在debug的时候发现被
ViewNameMethodReturnValueHandler抢先了,因为我的返回值是一个string类型的对象。
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}