Spring MVC非常灵活,在使用的时候可以返回视图,也可以直接返回普通数据,在想,内部是怎么实现的呢?
经过了几天研究Spring MVC的源码,可以看前几篇文章,今天再弄明白下为什么有时候返回视图,有时候直接返回数据呢。
分析
- 首先配置web.xml并且准备好视图,做一些准备工作
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
dispatcher-servlet.xml中视图配置
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
Controller类
@RequestMapping("/hello")
@ResponseBody
public String hello(){
return "hello";
}
@RequestMapping("/index")
public String indexView(){
return "index";
}
image.png
- 访问进行调试,关于DispatcherServlet的流程,我们前面的文章已经都说过了,这里不做太多的赘述,getHandler, getHandlerAdapter,基本不变,在HandlerAdapter.handler(Handler)的时候内部有些不同。
返回视图与返回数据刚开始步骤基本都一样,invoke返回的值也是相同的。
真正产生变化的地方,在处理最后的返回值时
image.png- 处理返回值,在激发方法之后,要决定要用什么返回值处理器来处理返回的类型。其中涉及到一个接口HandlerMethodReturnValueHandler,真正用来处理的。
HandlerMethodReturnValueHandler有两个方法:
- supportsReturnType是否支持当前的返回值类型,如果支持的话使用当前的HandlerMethodReturnValueHandler处理
- handleReturnValue,处理返回值
/**
* 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);
if (handler == null) {
throw new IllegalArgumentException("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;
}
image.png
如果是@ResponseBody注解的时候由RequestResponseBodyMethodProcessor处理。
//RequestResponseBodyMethodProcessor
@Override
public boolean supportsReturnType(MethodParameter returnType) {
//判断是否有ResponseBody.class的注解
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
返回值是String,但不加@ResponseBody注解的时候由ViewNameMethodReturnValueHandler处理。
// ViewNameMethodReturnValueHandler
@Override
public boolean supportsReturnType(MethodParameter returnType) {
//参数类型如果是void或者可以CharSequence.class.isAssignableFrom
Class<?> paramType = returnType.getParameterType();
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}
如果返回值为ModelAndView的时候,由ModelAndViewMethodReturnValueHandler处理
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
}
其余的可以自行查看。
- 上一步因为不同的ReturnValueHandler处理returnValue的结果不一样,Spring可以进行下一步的操作。
对于返回数据类型的操作,其将mavContainer的requestHandled设置为true,然后ModelAndView就会返回为null,后续的处理就不按照视图来处理。
如果返回的是视图,那么requestHandled仍然为false,后续再processDispatchResult中进行render.
- 后续和之前提到的DispatcherServlet都一样了。
回顾
Spring MVC对不同的返回值处理的方式不同,依靠的是HandlerMethodReturnValueHandler的不同实现。
主要在invoke方法之后,返回值与刚才激发的方法都被封装在ServletInvocableHandlerMethod里面了,其可以通过Method对象获取加在方法上的注解,类等等信息,最后做出结果。
最后
抓住DispatcherServlet的大体框架之后,其中的细节就简答的多了。
网友评论