基于Spring5.0.6版本。
一、SpringMVC整体流程
public class DispatcherServlet extends FrameworkServlet {
/** List of HandlerMappings used by this servlet */
@Nullable
private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet */
@Nullable
private List<HandlerAdapter> handlerAdapters;
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
ModelAndView mv = null;
Exception dispatchException = null;
// Determine handler for the current request.
//从所有的HandlerMapping接口实现类中,获取需要的HandlerExecutionChain对象实例
//第1步
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//第2步
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//第3步
//调用拦截器
//处理器拦截器的预处理(正序执行)
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//第4步
// Actually invoke the handler.
//处理器适配器调用我们的处理器
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//第5步
applyDefaultViewName(processedRequest, mv);
//第6步
//调用拦截器
//处理器拦截器的后处理(逆序)
mappedHandler.applyPostHandle(processedRequest, response, mv);
//第7步
//处理转发结果
//里面有调用视图渲染的方法,rend方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
}
SpringMVC,标准用法一般是配置DispatcherServlet类作为前端控制转发器,拦截所有的请求。当请求被拦截后,最终会调用doDispatch方法。这里所指的整体流程,是doDispatch的整体流程。
doDispatch方法内,会遵循如下流程:
第1步、通过getHandler方法获取HandlerExecutionChain类型的对象
HandlerExecutionChain类的属性如下,我们需要关心handler和interceptors属性。其中,handler是具体的请求处理类,interceptors是相应的拦截链。通过这个对象,系统可以知道具体的执行处理类是哪个,在执行处理类前后的拦截器都有哪些,并可以执行相应的拦截器方法。注意,基于Spring5.0.6版本的HandlerInterceptor的实现,引入了JDK1.8的新特性,default关键字,接口可以拥有方法体。
getHandler方法内部,会迭代容器内注册的所有的实现了HandlerMapping接口的实现类。一般可能有RequestMappingHandlerMapping、BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping三种。这个需要注意,RequestMappingHandlerMapping类返回的HandlerExecutionChain对象的handler的真实类型是HandlerMethod类的实例,他对应的是使用了@Controller和@RequestMapping注解的任意POJO。而BeanNameUrlHandlerMapping和SimpleUrlHandlerMapping返回的HandlerExecutionChain对象的handler的真实类型是实现了Controller接口的任意POJO。
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler;
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
}
public class DispatcherServlet extends FrameworkServlet {
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
}
public interface HandlerInterceptor {
/**
* Intercept the execution of a handler. Called after HandlerMapping determined
* an appropriate handler object, but before HandlerAdapter invokes the handler.
* <p>DispatcherServlet processes a handler in an execution chain, consisting
* of any number of interceptors, with the handler itself at the end.
* With this method, each interceptor can decide to abort the execution chain,
* typically sending a HTTP error or writing a custom response.
*/
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
/**
* Intercept the execution of a handler. Called after HandlerAdapter actually
* invoked the handler, but before the DispatcherServlet renders the view.
* Can expose additional model objects to the view via the given ModelAndView.
* <p>DispatcherServlet processes a handler in an execution chain, consisting
* of any number of interceptors, with the handler itself at the end.
* With this method, each interceptor can post-process an execution,
* getting applied in inverse order of the execution chain.
*/
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception {
}
/**
* Callback after completion of request processing, that is, after rendering
* the view. Will be called on any outcome of handler execution, thus allows
* for proper resource cleanup.
* <p>Note: Will only be called if this interceptor's {@code preHandle}
* method has successfully completed and returned {@code true}!
* <p>As with the {@code postHandle} method, the method will be invoked on each
* interceptor in the chain in reverse order, so the first interceptor will be
* the last to be invoked.
*/
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {
}
}
public interface Controller {
@Nullable
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
第2步、通过getHandlerAdapter方法获取HandlerAdapter接口的实例
public class DispatcherServlet extends FrameworkServlet {
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter ha : this.handlerAdapters) {
if (ha.supports(handler)) {
return ha;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
}
getHandlerAdapter方法内部,会迭代容器内注册的所有的实现了HandlerAdapter接口的实现类。一般可能有RequestMappingHandlerAdapter、SimpleControllerHandlerAdapter两种。其中,RequestMappingHandlerAdapter类的supports方法,在其父类AbstractHandlerMethodAdapter内。
public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
}
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
}
第3步、调用拦截链内的拦截器的preHandle方法
public class HandlerExecutionChain {
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
}
第4步、调用第2步获取的HandlerAdapter接口的实例的handle方法,方法返回值是ModelAndView类的实例
这里,以RequestMappingHandlerAdapter、SimpleControllerHandlerAdapter为例。
SimpleControllerHandlerAdapter(第2步列出该类的代码)的handle方法是直接调用实例的handleRequest方法,handleRequest是接口Controller定义的方法。对于SimpleControllerHandlerAdapter而言,Spring框架不提供参数的解析支持,有关参数的解析都在具体handleRequest接口方法的是实现体内,是程序员自己写的,主要应该是对request的对象的各种处理获取的。SimpleControllerHandlerAdapter的逻辑很简单,我们这里主要介绍RequestMappingHandlerAdapter类的内部处理逻辑。
RequestMappingHandlerAdapter方法内部,涉及到了参数的解析、处理器调用、返回值的处理等逻辑。
大体逻辑是,先解析参数。解析参数时,利用到了HandlerMethodArgumentResolver接口。从HandlerMethodArgumentResolver接口的实例列表中,找到对应的能解析参数的实例。具体的解析参数的逻辑,由HandlerMethodArgumentResolver接口的实现类决定。有的实现类内部可以直接解析参数,不需要借助其他辅助类。有的实现类的内部,在解析参数时,需要借助HttpMessageConverter接口的实现类,这时真正解析参数的是HttpMessageConverter接口的实现类的read方法。例如RequestResponseBodyMethodProcessor类,这个类支持有@RequestBody注解的参数的解析,它在解析参数时,会借助HttpMessageConverter接口。本质是调用HttpMessageConverter接口的T read(Class<? extends T> clazz, HttpInputMessage inputMessage)方法进行请求参数的解析和转换的。
在解析参数时,是否需要消息转换,完全取决于HandlerMethodArgumentResolver接口的实例的实现。有的实现,需要消息转换,有的实现,不需要消息转换。例如,RequestParamMethodArgumentResolver就不需要消息转换。这里可以参考,这篇文章中有更详细的介绍。
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
处理器的调用,涉及到的反射机制。因为RequestMappingHandlerAdapter是对HandlerMethod类型的handler的适配。
public class InvocableHandlerMethod extends HandlerMethod {
protected Object doInvoke(Object... args) throws Exception {
//调用具体的业务方法,可能是controller类的方法
return getBridgedMethod().invoke(getBean(), args);
}
}
public class HandlerMethod {
private final Object bean;
@Nullable
private final BeanFactory beanFactory;
private final Class<?> beanType;
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
protected Method getBridgedMethod() {
return this.bridgedMethod;
}
}
返回值的处理,涉及到了HandlerMethodReturnValueHandler接口。处理逻辑和解析参数类似。通过RequestMappingHandlerAdapter代码可以看出,RequestMappingHandlerAdapter返回的ModelAndView类的对象,并没有直接利用处理类的方法的返回值,而是通过ModelAndViewContainer类作为处理类方法的参数和返回值ModelAndView类的实例之间的桥梁。
public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter{
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
mav = invokeHandlerMethod(request, response, handlerMethod);
return mav;
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//init mavContainer 省略
//实际处理类
invocableMethod.invokeAndHandle(webRequest, mavContainer);
//构建返回值ModelAndView对象,是通过mavContainer对象传递参数的
return getModelAndView(mavContainer, modelFactory, webRequest);
}
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
return new ServletInvocableHandlerMethod(handlerMethod);
}
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
//init mav 省略
return mav;
}
}
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
}
这里明确一下,返回值的处理和页面渲染,没有必然联系。返回值的处理,依赖的是HandlerMethodReturnValueHandler接口的实现。渲染,依赖于ViewResolver和View接口的实现类。
例如,如果返回值处理使用的是RequestResponseBodyMethodProcessor(同时实现了HandlerMethodArgumentResolver接口)类,那么后续是不需要页面渲染的。假设返回值类型是String类型时。RequestResponseBodyMethodProcessor类支持返回值使用了@RequestBody注解的方法。RequestResponseBodyMethodProcessor类的handleReturnValue方法的第一行,mavContainer.setRequestHandled(true);,这段代码将导致DispatcherServlet类的doDispatch方法的后续渲染页面时,什么都不做(后续页面渲染时会有相关介绍)。writeWithMessageConverters方法内部,是对返回值的实际处理逻辑。对返回值的解析处理,和对请求参数的解析处理逻辑类似。可能依赖于HttpMessageConverter接口的实现,可能不依赖,这个完全取决于HandlerMethodReturnValueHandler接口的实现类的具体实现。对于本例中的RequestResponseBodyMethodProcessor类,他依赖于HttpMessageConverter接口,所以他对返回值的最终处理逻辑,其实是在对应的HttpMessageConverter接口的实例的void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)方法内。针对本例的返回值类型是String,需要的HttpMessageConverter接口的实例是StringHttpMessageConverter类,具体的返回值处理在相应的write方法内部。最终可以看出,利用StreamUtils类的copy方法,在方法内部利用OutputStreamWriter对象,writer.write直接将返回结果输出到请求方。
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
//对应的handler类含有ResponseBody注解或对应的handler类下的method含有ResponseBody注解
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||returnType.hasMethodAnnotation(ResponseBody.class));
}
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
......
return adaptArgumentIfNecessary(arg, parameter);
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
implements HandlerMethodReturnValueHandler {
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage,
ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
......
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
//添加注解结束
if (genericConverter != null ?((GenericHttpMessageConverter) converter).canWrite(declaredType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
genericConverter.write(outputValue, declaredType, selectedMediaType, outputMessage);
}
return;
}
}
}
public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> {
@Override
public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
writeInternal(t, outputMessage);
outputMessage.getBody().flush();
}
}
public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
@Override
public boolean supports(Class<?> clazz) {
return String.class == clazz;
}
@Override
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {
if (this.writeAcceptCharset) {
outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets());
}
Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
StreamUtils.copy(str, charset, outputMessage.getBody());
}
}
public abstract class StreamUtils {
public static void copy(String in, Charset charset, OutputStream out) throws IOException {
Assert.notNull(in, "No input String specified");
Assert.notNull(charset, "No charset specified");
Assert.notNull(out, "No OutputStream specified");
Writer writer = new OutputStreamWriter(out, charset);
writer.write(in);
writer.flush();
}
}
第5步、调用applyDefaultViewName方法,用于view名称转换
该方法用于view名称的转换,如果mv参数不为空,同时mv的view属性为空的,该方法会为mv设置默认的viewName值。例如,如果请求地址是http://localhost:8082/standard/hello/returnvoid?name=123,那么getDefaultViewName返回的值为/standard/hello/returnvoid,即defaultViewName为/standard/hello/returnvoid。
以上面的RequestMappingHandlerAdapter和RequestResponseBodyMethodProcessor为例,因为RequestResponseBodyMethodProcessor类的handleReturnValue方法内部,第一段代码mavContainer.setRequestHandled(true);的缘故,导致RequestMappingHandlerAdapter的getModelAndView方法返回的是null,即applyDefaultViewName方法的mv参数为null,这会导致applyDefaultViewName什么都不做。
public class DispatcherServlet extends FrameworkServlet {
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
/**
* 针对请求路径http://localhost:8082/standard/hello/returnvoid?name=123,
* 得到的默认值是/standard/hello/returnvoid
*/
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
}
public class ModelAndView {
@Nullable
private Object view;
/** Model Map */
@Nullable
private ModelMap model;
/** Optional HTTP status for the response */
@Nullable
private HttpStatus status;
public boolean hasView() {
return (this.view != null);
}
}
解析上面的http://localhost:8082/standard/hello/returnvoid?name=123的例子,匹配的controller如下。针对index2方法,匹配的返回值处理类是ViewNameMethodReturnValueHandler。ViewNameMethodReturnValueHandler类的handleReturnValue方法内,针对void返回值的情况,对mavContainer参数未作任何处理。这导致RequestMappingHandlerAdapter的getModelAndView方法,返回的是ModelAndView对象不为null,但是 对象的view属性为null。所以这里的第5步applyDefaultViewName会将ModelAndView对象的ViewName属性设置为默认值,即/standard/hello/returnvoid。这种情况下,如果/WEB-INF/view/standard/hello/returnvoid.jsp文件不存在,页面就会报出404错误。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- AnnotationDrivenBeanDefinitionParser 注册HandlerMapper、HandlerAdapter两个映射类 -->
<mvc:annotation-driven />
<!-- 配置扫描的包 -->
<context:component-scan base-package="com.*.*" />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
@Controller
@RequestMapping("/standard/hello")
public class StandardHelloController {
@RequestMapping("/returnvoid")
public void index2(String name) {
logger.info("returnvoid run here");
}
}
public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else if (returnValue != null){
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
}
第6步、拦截器后处理,mappedHandler.applyPostHandle(processedRequest, response, mv);
public class HandlerExecutionChain {
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
}
第7步、processDispatchResult,处理结果,里面有相应的渲染方法和对拦截链内的拦截器的afterCompletion方法的调用
从processDispatchResult方法内部可以代码逻辑可以看出,mv为nul是,SpringMVC不做渲染。
public class DispatcherServlet extends FrameworkServlet {
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
//视图的渲染
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
//在这里进行视图渲染
render(mv, request, response);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() + "': assuming HandlerAdapter completed request handling");
}
}
//触发整个请求处理完毕回调方法afterCompletion
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
//这里解析视图对象,从而获取真正的视图
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
}
//这里进行真正的视图渲染
view.render(mv.getModelInternal(), request, response);
}
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
}
public class HandlerExecutionChain {
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
}
有关试图渲染,主要涉及到两个接口,ViewResolver和View接口。ViewResolver对应视图解析器,View对应视图,下面将一一介绍。思路是利用ViewResolver接口的是实现类,找到对应的视图实现类,然后由视图实现类的render方法去负责具体的页面渲染工作。
【视图解析器】
public interface ViewResolver {
@Nullable
View resolveViewName(String var1, Locale var2) throws Exception;
}
- SpringMVC为逻辑视图名的解析提供了不同的策略,可以在Spring Web 上下文中配置一种或多种解析策略,并指定他们之间的先后顺序。
- 每一种映射策略对应一个具体的视图解析器实现类。
- 视图解析器的作用比较单一:将逻辑视图解析为一个具体的视图对象。
- 所有的视图解析器都必须实现ViewResolver接口。
- 常见的视图解析器实现类
- 可以选择一种或多种视图解析器,可以通过其order属性指定解析器的优先顺序,order越小优先级越高。
- SpringMVC会按照视图解析器顺序的优先次序进行解析,直到返回视图对象。若无,则抛出ServletException异常。
【视图】
public interface View {
String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
String PATH_VARIABLES = View.class.getName() + ".pathVariables";
String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";
@Nullable
default String getContentType() {
return null;
}
void render(@Nullable Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
}
- 视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户;
- 为了实现视图模型和具体实现技术的解耦,Spring在org.springframwork.web.servlet包中定义了一个高度抽象的View接口
- 视图对象由视图解析器负责实例化,由于他们是无状态的,所以不存在线程安全的问题。
- 常见的视图实现类
JstlView extends InternalResourceView
这里,我们通过xml文件的配置,指定的试图解析器是InternalResourceViewResolver,该解析器对应的视图是InternalResourceView。下面,将以这种配置,介绍有关jsp的视图渲染。
public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {
public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);
this.prepareResponse(request, response);
this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
}
protected void exposeModelAsRequestAttributes(Map<String, Object> model,HttpServletRequest request) throws Exception {
model.forEach((modelName, modelValue) -> {
if (modelValue != null) {
request.setAttribute(modelName, modelValue);
}
else {
request.removeAttribute(modelName);
}
});
}
}
public abstract class AbstractUrlBasedView extends AbstractView implements InitializingBean {
@Nullable
private String url;
@Nullable
public String getUrl() {
return this.url;
}
}
public class InternalResourceView extends AbstractUrlBasedView {
@Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//model在request中以("key":value)形式存在,request.setAttribute将参数置入请求中
//exposeModelAsRequestAttributes的实现在父类抽象类AbstractView内
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
// If already included or response already committed, perform include, else forward.
if (useInclude(request, response)) {
response.setContentType(getContentType());
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
rd.forward(request, response);
}
}
protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response) throws Exception {
//调用父类AbstractUrlBasedView类的getUrl方法
String path = getUrl();
return path;
}
@Nullable
protected RequestDispatcher getRequestDispatcher(HttpServletRequest request, String path) {
return request.getRequestDispatcher(path);
}
}
对于jsp而言,到了RequestDispatcher类的forward和include方法,就可以认为渲染结束的,剩下的就是容器的工作了。其他形式的界面渲染,这里不做介绍。其实,现在前后端分离是趋势,界面渲染已经用的越来越少了。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package javax.servlet;
import java.io.IOException;
public interface RequestDispatcher {
String FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
String FORWARD_CONTEXT_PATH = "javax.servlet.forward.context_path";
String FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
String FORWARD_QUERY_STRING = "javax.servlet.forward.query_string";
String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
String INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
String ERROR_EXCEPTION = "javax.servlet.error.exception";
String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
String ERROR_MESSAGE = "javax.servlet.error.message";
String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
String ERROR_STATUS_CODE = "javax.servlet.error.status_code";
void forward(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
void include(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
}