简单分析Spring MVC源码
一、DispatcherServlet的继承关系
二、DispatcherServlet收到请求的一个大致流程
三、测试代码
1、springmvc.xml
<?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: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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 开启组件扫描 -->
<context:component-scan base-package="cn.ilqjx"/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
2、success.jsp
<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<html>
<head>
<title>success</title>
</head>
<body>
<%
System.out.println("调用目标页面 success.jsp");
%>
</body>
</html>
3、HelloController
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello() {
System.out.println("调用目标方法 hello()");
return "success";
}
}
四、确定目标方法和目标页面的调用位置
1、DispatcherServlet#doDispatch源码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler.
// 调用处理器的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
// 转发到目标页面
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
2、目标方法和目标页面的调用位置
// 调用处理器的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 转发到目标页面
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
五、分析DispatcherServlet#doDispatch
1、分析
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检查是否是文件上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// Determine handler for the current request.
// 确定当前请求的处理器
/*
mappedHandler = {HandlerExecutionChain@5606}
// HelloController 处理器
handler = {HelloController@5946}
interceptors = {HandlerInterceptor[1]@5947}
interceptorList = {ArrayList@5948} size = 1
interceptorIndex = 0
*/
mappedHandler = getHandler(processedRequest);
// 如果找不到处理器,就会抛异常或404
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 确定当前请求的处理器适配器
// ha: AnnotationMethodHandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler.
// 适配器执行目标方法并将返回值作为视图名封装到 ModelAndView 中
// 无论目标方法怎么写,最终适配器执行完目标方法后,都会将执行后的信息封装成 ModelAndView
/*
mv = {ModelAndView@5950}
// 视图名
view = "success"
model = {ModelMap@5957} size = 0
cleared = false
*/
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
// 如果没有视图名,设置一个默认的视图名。比如:目标方法的返回值为 void,视图名为请求地址
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
// 根据目标方法执行完后封装的 ModelAndView 转发到目标页面,而且 ModelAndView 中的数据可以从请求域中获取。
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
2、小总结
DispatcherServlet收到请求调用doDispatch()方法进行处理
(1)、getHandler():根据当前请求地址找到能处理这个请求的目标处理器
根据当前请求在 HandlerMapping 中找到这个请求的映射信息,获取到目标处理器。
(2)、getHandlerAdapter():根据当前处理器获取到能执行这个处理器方法的适配器
根据当前处理器,获取到相应的 HandlerAdapter。
(3)、handle():使用适配器执行目标方法并返回一个 ModelAndView 对象
(4)、processDispatchResult():根据 ModelAndView 的信息转发到具体的页面,并且可以从请求域中取出 ModelAndView 中的模型数据
六、细节分析
1、getHandler()细节:如何根据当前请求找到对应的处理器
1.1、getHandler()源码
getHandler()返回目标处理器的执行链
mappedHandler = getHandler(processedRequest);
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
1.2、分析getHandler()
HandlerMapping:处理器映射,保存了每一个处理器能够处理哪些请求的映射信息
BeanNameUrlHandlerMapping:处理 xml 配置的
DefaultAnnotationHandlerMapping:处理注解的
handlerMap:ioc 容器启动创建 Controller 对象的时候扫描每个处理器都能处理什么请求,保存在 handlerMap 属性中。下一次请求过来,就会查看哪个 HandlerMapping 中有请求映射信息。
2、getHandlerAdapter()细节:如何根据当前处理器找到相应的适配器
2.1、getHandlerAdapter()源码
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
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");
}
2.2、分析getHandlerAdapter()
AnnotationMethodHandlerAdapter:解析注解方法的适配器
七、Spring MVC的九大组件
Spring MVC在工作的时候,关键位置都是由这些组件完成的。
共同点:九大组件都是接口,接口就是规范,提供了非常强大的扩展性。
/** 文件上传解析器 */
private MultipartResolver multipartResolver;
/** 区域信息解析器(与国际化相关) */
private LocaleResolver localeResolver;
/** 主题解析器(强大的主题效果更换) */
private ThemeResolver themeResolver;
/** Handler 映射信息 */
private List<HandlerMapping> handlerMappings;
/** Handler 的适配器 */
private List<HandlerAdapter> handlerAdapters;
/** 异常解析器(Spring MVC强大的异常解析功能) */
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** 从 request 中获取视图名 */
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMap + Manager:Spring MVC中允许重定向携带数据的功能 */
private FlashMapManager flashMapManager;
/** 视图解析器 */
private List<ViewResolver> viewResolvers;
1、九大组件初始化的地方
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
2、分析九大组件初始化方法之 initLocaleResolver(),其他类似
有些组件在容器中是使用 id 进行查找的,有些组件是使用类型进行查找的。
去容器中查找这个组件,如果没找到就使用默认的配置。
private void initLocaleResolver(ApplicationContext context) {
try {
// public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
// 根据 id 从 ioc 容器中获取
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Using LocaleResolver [" + this.localeResolver + "]");
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
// 如果没获取到,使用默认配置
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver + "]");
}
}
}
3、默认配置
/**
* Name of the class path resource (relative to the DispatcherServlet class)
* that defines DispatcherServlet's default strategy names.
*/
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
4、可以在web.xml中修改DispatcherServlet某些属性的默认配置
/** Detect all HandlerMappings or just expect "handlerMapping" bean? */
private boolean detectAllHandlerMappings = true;
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>detectAllHandlerMappings</param-name>
<param-value>false</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
八、执行目标方法的细节
// 执行目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Class<?> clazz = ClassUtils.getUserClass(handler);
Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
if (annotatedWithSessionAttributes == null) {
annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
}
if (annotatedWithSessionAttributes) {
// Always prevent caching in case of session attribute management.
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
// Prepare cached set of session attributes names.
}
else {
// Uses configured default cacheSeconds setting.
checkAndPrepare(request, response, true);
}
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handler);
}
}
}
// 执行目标方法
return invokeHandlerMethod(request, response, handler);
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 获取方法解析器
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
// 方法解析器根据当前请求地址找到目标方法
// handlerMethod: public java.lang.String cn.ilqjx.controller.HelloController.hello()
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
// 创建方法执行器
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
// 对原生的 request 和 response 进行包装
ServletWebRequest webRequest = new ServletWebRequest(request, response);
// 创建隐含模型
ExtendedModelMap implicitModel = new BindingAwareModelMap();
// 真正执行目标方法;目标方法利用反射执行期间确定参数值,提前执行标了 @ModelAttribute 注解的方法等所有的操作都在这个方法中
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
}
1、测试代码
1.1、form表单
<form action="/springmvc/updateBook" method="post">
书名:<input type="text" name="bookName"/> <br/>
作者:<input type="text" name="author"/> <br/>
<input type="submit" value="更新图书"/>
</form>
1.2、BookController
为了观察如何给目标方法的参数赋值,所以更换测试代码。
@Controller
public class BookController {
@RequestMapping("/updateBook")
public String updateBook(@RequestParam("author") String author, Map<String, Object> map,
HttpServletRequest request, @ModelAttribute("book") Book book) {
System.out.println("更新后的 book: " + book);
System.out.println("调用了 updateBook() 方法");
return "success";
}
@ModelAttribute
public void init(Map<String, Object> map) {
Book book = new Book("如何让富婆爱上我", "无名");
map.put("book", book);
System.out.println("调用了 init() 方法");
}
}
2、执行目标方法
// 真正执行目标方法
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
// 获取到要要执行的处理器方法
// public java.lang.String cn.ilqjx.controller.BookController.updateBook(java.lang.String,java.util.Map,javax.servlet.http.HttpServletRequest,cn.ilqjx.pojo.Book)
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
try {
boolean debug = logger.isDebugEnabled();
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
if (attrValue != null) {
implicitModel.addAttribute(attrName, attrValue);
}
}
// 获取标注了 @ModelAttribute 注解的所有方法
for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
// 我们的测试代码中只有 init() 方法标注了 @ModelAttribute 注解
// public void cn.ilqjx.controller.BookController.init(java.util.Map)
Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
// 确定标注了@ModelAttribute注解的方法执行时要使用的每一个参数的值(见2.1)
Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
}
// 分析 attrName 的值(见2.4)
String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
continue;
}
// 设置目标方法是可访问的
ReflectionUtils.makeAccessible(attributeMethodToInvoke);
// 执行标注了 @ModelAttribute 注解的方法(先于目标方法运行)
Object attrValue = attributeMethodToInvoke.invoke(handler, args);
if ("".equals(attrName)) {
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
}
// @ModelAttribute注解的另一个作用(见2.5)
// 把提前运行的标注了@ModelAttribute注解的方法的返回值放在隐含模型中
if (!implicitModel.containsAttribute(attrName)) {
implicitModel.addAttribute(attrName, attrValue);
}
}
// 确定目标方法执行时要使用的每一个参数的值(见2.6,和2.1是同一个方法,在详细分析一下)
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
}
// 设置为可访问的
ReflectionUtils.makeAccessible(handlerMethodToInvoke);
// 执行目标方法
return handlerMethodToInvoke.invoke(handler, args);
}
catch (IllegalStateException ex) {
// Internal assertion failed (e.g. invalid signature):
// throw exception with full handler method context...
throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
}
catch (InvocationTargetException ex) {
// User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
ReflectionUtils.rethrowException(ex.getTargetException());
return null;
}
}
2.1、确定方法执行时要使用的每一个参数的值
下面的分析参照此测试代码
@ModelAttribute
public void init(Map<String, Object> map) {
Book book = new Book("如何让富婆爱上我", "无名");
map.put("book", book);
System.out.println("调用了 init() 方法");
}
// 确定方法执行时要使用的每一个参数的值
Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
Class<?>[] paramTypes = handlerMethod.getParameterTypes();
// 创建了一个长度和参数个数一样多的数组,会用来保存每一个参数的值
Object[] args = new Object[paramTypes.length];
// 遍历方法的所有参数
for (int i = 0; i < args.length; i++) {
MethodParameter methodParam = new MethodParameter(handlerMethod, i);
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
String paramName = null;
String headerName = null;
boolean requestBodyFound = false;
String cookieName = null;
String pathVarName = null;
String attrName = null;
boolean required = false;
String defaultValue = null;
boolean validate = false;
Object[] validationHints = null;
int annotationsFound = 0;
// 获取方法这个参数的所有注解
Annotation[] paramAnns = methodParam.getParameterAnnotations();
// 如果有注解就解析并保存注解的信息
for (Annotation paramAnn : paramAnns) {
if (RequestParam.class.isInstance(paramAnn)) {
RequestParam requestParam = (RequestParam) paramAnn;
paramName = requestParam.value();
required = requestParam.required();
defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
annotationsFound++;
}
else if (RequestHeader.class.isInstance(paramAnn)) {
RequestHeader requestHeader = (RequestHeader) paramAnn;
headerName = requestHeader.value();
required = requestHeader.required();
defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
annotationsFound++;
}
else if (RequestBody.class.isInstance(paramAnn)) {
requestBodyFound = true;
annotationsFound++;
}
else if (CookieValue.class.isInstance(paramAnn)) {
CookieValue cookieValue = (CookieValue) paramAnn;
cookieName = cookieValue.value();
required = cookieValue.required();
defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
annotationsFound++;
}
else if (PathVariable.class.isInstance(paramAnn)) {
PathVariable pathVar = (PathVariable) paramAnn;
pathVarName = pathVar.value();
annotationsFound++;
}
else if (ModelAttribute.class.isInstance(paramAnn)) {
ModelAttribute attr = (ModelAttribute) paramAnn;
attrName = attr.value();
annotationsFound++;
}
else if (Value.class.isInstance(paramAnn)) {
defaultValue = ((Value) paramAnn).value();
}
else if (paramAnn.annotationType().getSimpleName().startsWith("Valid")) {
validate = true;
Object value = AnnotationUtils.getValue(paramAnn);
validationHints = (value instanceof Object[] ? (Object[]) value : new Object[] {value});
}
}
if (annotationsFound > 1) {
throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
"do not specify more than one such annotation on the same parameter: " + handlerMethod);
}
// 没有注解的情况
if (annotationsFound == 0) {
// 解析普通参数(见2.2)
Object argValue = resolveCommonArgument(methodParam, webRequest);
if (argValue != WebArgumentResolver.UNRESOLVED) {
args[i] = argValue;
}
else if (defaultValue != null) {
args[i] = resolveDefaultValue(defaultValue);
}
else {
// 获取参数类型
Class<?> paramType = methodParam.getParameterType();
// 如果是 Model 类型或者是 Map类型,则将之前创建的隐含模型赋值给这个参数
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
if (!paramType.isAssignableFrom(implicitModel.getClass())) {
throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " +
"Model or Map but is not assignable from the actual model. You may need to switch " +
"newer MVC infrastructure classes to use this argument.");
}
// 将隐含模型赋值给这个参数
args[i] = implicitModel;
}
else if (SessionStatus.class.isAssignableFrom(paramType)) {
args[i] = this.sessionStatus;
}
else if (HttpEntity.class.isAssignableFrom(paramType)) {
args[i] = resolveHttpEntityRequest(methodParam, webRequest);
}
else if (Errors.class.isAssignableFrom(paramType)) {
throw new IllegalStateException("Errors/BindingResult argument declared " +
"without preceding model attribute. Check your handler method signature!");
}
else if (BeanUtils.isSimpleProperty(paramType)) {
paramName = "";
}
else {
attrName = "";
}
}
}
if (paramName != null) {
args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
}
else if (headerName != null) {
args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
}
else if (requestBodyFound) {
args[i] = resolveRequestBody(methodParam, webRequest, handler);
}
else if (cookieName != null) {
args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
}
else if (pathVarName != null) {
args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
}
else if (attrName != null) {
WebDataBinder binder =
resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
if (binder.getTarget() != null) {
doBind(binder, webRequest, validate, validationHints, !assignBindingResult);
}
args[i] = binder.getTarget();
if (assignBindingResult) {
args[i + 1] = binder.getBindingResult();
i++;
}
implicitModel.putAll(binder.getBindingResult().getModel());
}
}
return args;
}
2.2、解析普通参数
如果没有注解,先看是否是普通参数,确定当前的参数是否是原生API。
// 解析普通参数
Object argValue = resolveCommonArgument(methodParam, webRequest);
protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
throws Exception {
// Invoke custom argument resolvers if present...
if (this.customArgumentResolvers != null) {
for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) {
Object value = argumentResolver.resolveArgument(methodParameter, webRequest);
if (value != WebArgumentResolver.UNRESOLVED) {
return value;
}
}
}
// Resolution of standard parameter types...
Class<?> paramType = methodParameter.getParameterType();
// 确定当前的参数是否是原生API(见2.3)
Object value = resolveStandardArgument(paramType, webRequest);
if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) {
throw new IllegalStateException("Standard argument type [" + paramType.getName() +
"] resolved to incompatible value of type [" + (value != null ? value.getClass() : null) +
"]. Consider declaring the argument type in a less specific fashion.");
}
return value;
}
2.3、确定当前的参数是否是原生API
// 确定当前的参数是否是原生API
Object value = resolveStandardArgument(paramType, webRequest);
protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
if (ServletRequest.class.isAssignableFrom(parameterType) ||
MultipartRequest.class.isAssignableFrom(parameterType)) {
Object nativeRequest = webRequest.getNativeRequest(parameterType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + parameterType.getName() + "]: " + request);
}
return nativeRequest;
}
else if (ServletResponse.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
Object nativeResponse = webRequest.getNativeResponse(parameterType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + parameterType.getName() + "]: " + response);
}
return nativeResponse;
}
else if (HttpSession.class.isAssignableFrom(parameterType)) {
return request.getSession();
}
else if (Principal.class.isAssignableFrom(parameterType)) {
return request.getUserPrincipal();
}
else if (Locale.class.equals(parameterType)) {
return RequestContextUtils.getLocale(request);
}
else if (InputStream.class.isAssignableFrom(parameterType)) {
return request.getInputStream();
}
else if (Reader.class.isAssignableFrom(parameterType)) {
return request.getReader();
}
else if (OutputStream.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getOutputStream();
}
else if (Writer.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getWriter();
}
return super.resolveStandardArgument(parameterType, webRequest);
}
2.4、分析attrName的值
// 如果@ModelAttribute(value = "hello")注解value有值,attrName就等于value的值,所以attrName="hello"
// 如果@ModelAttribute(value = "hello")注解value没值,attrName就等于空串,所以attrName=""
String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
continue;
}
ReflectionUtils.makeAccessible(attributeMethodToInvoke);
Object attrValue = attributeMethodToInvoke.invoke(handler, args);
if ("".equals(attrName)) {
// 获取标注了@ModelAttribute注解的方法的返回值类型
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
// attrName就等于方法返回值类型首字母小写
attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
}
总结:
@ModelAttribute(value = “hello”)
public void init(Map<String, Object> map) {}
如果value有值,那么attrName=value的值;
如果value没值,那么attrName=方法返回值类型首字母小写,比如这个方法attrName=“void”。
2.5、 @ModelAttribute注解的另一个作用
// 可以把方法运行后的返回值按照指定的key放在隐含模型中,这个指定的key就是attrName(attrName的值见2.4)
if (!implicitModel.containsAttribute(attrName)) {
implicitModel.addAttribute(attrName, attrValue);
}
2.6、确定方法执行时要使用的每一个参数的值(详细分析)
下面的分析参照此测试代码
@RequestMapping("/updateBook")
public String updateBook(@RequestParam("author") String author, Map<String, Object> map,
HttpServletRequest request, @ModelAttribute("book") Book book) {
System.out.println("更新后的 book: " + book);
System.out.println("调用了 updateBook() 方法");
return "success";
}
// 确定方法执行时要使用的每一个参数的值
Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
// 获取方法参数的每一个类型
/*
paramTypes
0 = {Class@341} "class java.lang.String"
1 = {Class@305} "interface java.util.Map"
2 = {Class@3242} "interface javax.servlet.http.HttpServletRequest"
3 = {Class@8233} "class cn.ilqjx.pojo.Book"
*/
Class<?>[] paramTypes = handlerMethod.getParameterTypes();
// 创建了一个长度和参数个数一样多的数组,会用来保存每一个参数的值
Object[] args = new Object[paramTypes.length];
// 遍历方法的所有参数
for (int i = 0; i < args.length; i++) {
MethodParameter methodParam = new MethodParameter(handlerMethod, i);
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
String paramName = null;
String headerName = null;
boolean requestBodyFound = false;
String cookieName = null;
String pathVarName = null;
String attrName = null;
boolean required = false;
String defaultValue = null;
boolean validate = false;
Object[] validationHints = null;
int annotationsFound = 0;
// 获取方法这个参数的所有注解
Annotation[] paramAnns = methodParam.getParameterAnnotations();
// 如果有注解就解析并保存注解的信息
for (Annotation paramAnn : paramAnns) {
// 第一个参数 @RequestParam("author") String author
if (RequestParam.class.isInstance(paramAnn)) {
RequestParam requestParam = (RequestParam) paramAnn;
// paramName = "author"
paramName = requestParam.value();
// required = true(默认)
required = requestParam.required();
// defaultValue = null(默认)
defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
// 标注在这个参数上的注解的个数加1
annotationsFound++;
}
else if (RequestHeader.class.isInstance(paramAnn)) {
RequestHeader requestHeader = (RequestHeader) paramAnn;
headerName = requestHeader.value();
required = requestHeader.required();
defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
annotationsFound++;
}
else if (RequestBody.class.isInstance(paramAnn)) {
requestBodyFound = true;
annotationsFound++;
}
else if (CookieValue.class.isInstance(paramAnn)) {
CookieValue cookieValue = (CookieValue) paramAnn;
cookieName = cookieValue.value();
required = cookieValue.required();
defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
annotationsFound++;
}
else if (PathVariable.class.isInstance(paramAnn)) {
PathVariable pathVar = (PathVariable) paramAnn;
pathVarName = pathVar.value();
annotationsFound++;
}
// 第四个参数 @ModelAttribute("book") Book book
else if (ModelAttribute.class.isInstance(paramAnn)) {
ModelAttribute attr = (ModelAttribute) paramAnn;
// 获取@ModelAttribute("book")注解的 value
// attrName = "book"
attrName = attr.value();
// 标注在这个参数上的注解的个数加1
annotationsFound++;
}
else if (Value.class.isInstance(paramAnn)) {
defaultValue = ((Value) paramAnn).value();
}
else if (paramAnn.annotationType().getSimpleName().startsWith("Valid")) {
validate = true;
Object value = AnnotationUtils.getValue(paramAnn);
validationHints = (value instanceof Object[] ? (Object[]) value : new Object[] {value});
}
}
// 只能在参数上标一个注解,多于1个抛异常
if (annotationsFound > 1) {
throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
"do not specify more than one such annotation on the same parameter: " + handlerMethod);
}
// 没有注解的情况
if (annotationsFound == 0) {
// 第三个参数 HttpServletRequest request,原生API(见2.3)
// 解析普通参数(见2.2)
Object argValue = resolveCommonArgument(methodParam, webRequest);
if (argValue != WebArgumentResolver.UNRESOLVED) {
args[i] = argValue;
}
else if (defaultValue != null) {
args[i] = resolveDefaultValue(defaultValue);
}
else {
// 获取参数类型
Class<?> paramType = methodParam.getParameterType();
// 第二个参数 Map<String, Object> map
// 如果是 Model 类型或者是 Map类型,则将之前创建的隐含模型赋值给这个参数
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
if (!paramType.isAssignableFrom(implicitModel.getClass())) {
throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " +
"Model or Map but is not assignable from the actual model. You may need to switch " +
"newer MVC infrastructure classes to use this argument.");
}
// 将隐含模型赋值给这个参数
args[i] = implicitModel;
}
else if (SessionStatus.class.isAssignableFrom(paramType)) {
args[i] = this.sessionStatus;
}
else if (HttpEntity.class.isAssignableFrom(paramType)) {
args[i] = resolveHttpEntityRequest(methodParam, webRequest);
}
else if (Errors.class.isAssignableFrom(paramType)) {
throw new IllegalStateException("Errors/BindingResult argument declared " +
"without preceding model attribute. Check your handler method signature!");
}
else if (BeanUtils.isSimpleProperty(paramType)) {
paramName = "";
}
else {
attrName = "";
}
}
}
if (paramName != null) {
// 确定参数的值(见2.7)
args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
}
else if (headerName != null) {
args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
}
else if (requestBodyFound) {
args[i] = resolveRequestBody(methodParam, webRequest, handler);
}
else if (cookieName != null) {
args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
}
else if (pathVarName != null) {
args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
}
// 确定自定义类型参数的值(第四个参数@ModelAttribute("book") Book book)(见2.8)
// 还要将请求中的每一个参数赋值给这个对象
else if (attrName != null) {
WebDataBinder binder =
resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
if (binder.getTarget() != null) {
doBind(binder, webRequest, validate, validationHints, !assignBindingResult);
}
// 从数据绑定器(WebDataBinder)中获取目标对象
args[i] = binder.getTarget();
if (assignBindingResult) {
args[i + 1] = binder.getBindingResult();
i++;
}
implicitModel.putAll(binder.getBindingResult().getModel());
}
}
return args;
}
小总结
标注解的参数:
保存注解的信息,最终得到这个注解应该对应解析的值。
没标注解的参数:
1、先判断是不是普通参数(是不是原生API),再判断是不是Model或者Map,如果是就放入隐含模型。
2、自定义类型的参数没有@ModelAttribute注解
(1)、先判断是不是原生API
(2)、再判断是不是Model或者Map
(3)、再判断是不是其他的类型,比如SessionStatus、HttpEntity、Errors
(4)、再判断是不是简单类型的属性,比如Integer,如果是paramName=""
(5)、attrName=""
2.7、确定参数的值
下面的分析参照此测试代码的第一个参数(@RequestParam(“author”) String author
@RequestMapping("/updateBook")
public String updateBook(@RequestParam("author") String author, Map<String, Object> map,
HttpServletRequest request, @ModelAttribute("book") Book book) {
System.out.println("更新后的 book: " + book);
System.out.println("调用了 updateBook() 方法");
return "success";
}
args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
private Object resolveRequestParam(String paramName, boolean required, String defaultValue,
MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
throws Exception {
// 获取参数类型
// paramType: class java.lang.String
Class<?> paramType = methodParam.getParameterType();
if (Map.class.isAssignableFrom(paramType) && paramName.length() == 0) {
return resolveRequestParamMap((Class<? extends Map<?, ?>>) paramType, webRequest);
}
if (paramName.length() == 0) {
paramName = getRequiredParameterName(methodParam);
}
Object paramValue = null;
MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(paramName);
if (!files.isEmpty()) {
paramValue = (files.size() == 1 ? files.get(0) : files);
}
}
// 为参数赋值
if (paramValue == null) {
// 获取参数值
// webRequest.getParameterValues(paramName) 多么熟悉的操作
String[] paramValues = webRequest.getParameterValues(paramName);
if (paramValues != null) {
// 此时 paramValue 就有值了
paramValue = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
if (paramValue == null) {
if (defaultValue != null) {
paramValue = resolveDefaultValue(defaultValue);
}
else if (required) {
raiseMissingParameterException(paramName, paramType);
}
paramValue = checkValue(paramName, paramValue, paramType);
}
WebDataBinder binder = createBinder(webRequest, null, paramName);
initBinder(handlerForInitBinderCall, paramName, binder, webRequest);
return binder.convertIfNecessary(paramValue, paramType, methodParam);
}
2.8、确定自定义类型参数的值
WebDataBinder binder =
resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
// Bind request parameter onto object...
// name = attrName = "book"
String name = attrName;
if ("".equals(name)) {
// 如果attrName是空串就将参数类型的首字母小写作为 key
// 注意:和变量名没有关系,比如@ModelAttribute Book bookTest,此时的 name = "book"
name = Conventions.getVariableNameForParameter(methodParam);
}
// paramType: class cn.ilqjx.pojo.Book
Class<?> paramType = methodParam.getParameterType();
// 目标对象
Object bindObject;
// 判断隐含模型中是否包含这个 key("book")
if (implicitModel.containsKey(name)) {
// 从隐含模型中获取,保存到目标对象
bindObject = implicitModel.get(name);
}
// 如果是@SessionAttributes注解标注的属性,就从session中获取
else if (this.methodResolver.isSessionAttribute(name, paramType)) {
bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
if (bindObject == null) {
raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
}
}
else {
// 如果没标@ModelAttribute这个注解,就创建一个新的对象
bindObject = BeanUtils.instantiateClass(paramType);
}
WebDataBinder binder = createBinder(webRequest, bindObject, name);
initBinder(handler, name, binder, webRequest);
return binder;
}
小总结
Spring MVC如何确定自定义类型的值:
1、如果隐含模型中有这个key(标了@ModelAttribute注解,就是这个注解的value属性;没标就是参数类型的首字母小写)指定的值,就将这个值赋值给bindObject。
2、如果是@SessionAttributes注解标注的属性,就从session中获取。
如果拿不到值就会抛异常,不建议使用@SessionAttributes这个注解。
3、如果都不是就利用反射创建对象
2.9、总结
确定方法参数:
如果参数有注解,就先保存注解的信息,最终得到这个注解应该对应解析的值。
1、先判断是不是原生API
2、再判断是不是Model或者Map
3、再判断是不是其他的类型,比如SessionStatus、HttpEntity、Errors
4、再判断是不是简单类型的属性,比如Integer,如果是paramName=""
5、给attrName赋值(标了@ModelAttribute(“xxx”)注解,就是指定的;没标就是"")
确定自定义类型参数的值:
(1)、如果隐含模型中有这个key(标了@ModelAttribute注解,就是这个注解的value属性;没标就是参数类型的首字母小写)指定的值,就将这个值赋值给bindObject。
2)、如果是@SessionAttributes注解标注的属性,就从session中获取。
如果拿不到值就会抛异常,不建议使用@SessionAttributes这个注解。
(3)、如果都不是就利用反射创建对象
6、拿到之前创建好的对象,使用数据绑定器(WebDataBinder)将请求中的每个数据保存到这个对象中。
九、视图解析器
1、任何方法的返回值最终都会被封装成ModelAndView对象
/*
mv = {ModelAndView@5672} "ModelAndView: reference to view with name 'success'; model is {}"
view = "success"
model = {ModelMap@5682} size = 0
cleared = false
*/
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
2、视图渲染流程
视图渲染流程:将域中的数据在页面展示;页面就是用来渲染模型数据的。
// 来到页面的方法
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
3、渲染页面
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
// 渲染页面
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
4、ViewResovler与View
ViewResolver的作用就是根据视图名(方法返回值)得到视图对象。
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
5、如何根据方法的返回值得到View对象?
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
// 遍历所有的 ViewResolver
for (ViewResolver viewResolver : this.viewResolvers) {
// ViewResolver 视图解析器根据方法的返回值得到一个 View 对象
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
6、创建View对象
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);
if (view == null) {
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
// 根据方法的返回值 viewName(视图名)创建 View 对象
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null) {
// 放缓存里了,下一次使用就不需要创建了
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
// 判断是否是 redirect 前缀
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
// 创建 RedirectView 对象
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
return applyLifecycleMethods(viewName, view);
}
// Check for special "forward:" prefix.
// 判断是否是 forward 前缀
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
// 创建 InternalResourceView 对象
return new InternalResourceView(forwardUrl);
}
// Else fall back to superclass implementation: calling loadView.
// 没有前缀就使用父类默认创建一个View
return super.createView(viewName, locale);
}
拼串得到页面地址
7、返回View对象
1、视图解析器得到View对象的流程就是,所有配置的视图解析器都来尝试根据视图名得到View对象;如果能得到就返回,得不到就下一个。
2、调用View对象的render()方法
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isTraceEnabled()) {
logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
" and static attributes " + this.staticAttributes);
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
// 渲染要给页面输出的所有数据
renderMergedOutputModel(mergedModel, request, response);
}
8、为什么请求域中能够获得隐含模型中的数据?
// 把隐含模型中的数据放在请求域中
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
for (Map.Entry<String, Object> entry : model.entrySet()) {
String modelName = entry.getKey();
Object modelValue = entry.getValue();
if (modelValue != null) {
// 答案
request.setAttribute(modelName, modelValue);
if (logger.isDebugEnabled()) {
logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
"] to request in view with name '" + getBeanName() + "'");
}
}
else {
request.removeAttribute(modelName);
if (logger.isDebugEnabled()) {
logger.debug("Removed model object '" + modelName +
"' from request in view with name '" + getBeanName() + "'");
}
}
}
}
9、总结
视图解析器只是为了得到视图对象,视图对象才能真正的转发(将隐含模型中的数据放在请求域中)或者重定向到页面,视图对象才能真正的渲染视图。