1. 数据输出
Spring MVC 提供了以下几种途径输出模型数据:
1.1 Map&Model
-
Spring MVC 在内部使用了一个 org.springframework.ui.Model 接口存储模型数据。
-
Spring MVC 在调用方法前会创建一个隐含的模型对象(BindingAwareModelMap) 作为模型数据的存储容器。如果方法形参为
org.springframework.ui.Model
、org.springframework.ui.ModelMap
或java.util.Map
类型,Spring MVC 会将隐含模型的引用传递给这些形参。在方法体内,开发者可以通过这个形参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。 -
底层其实都是 BindingAwareModelMap 在工作,而存在此对象中的数据最后都会被放在请求域中。
1.2 ModelAndView
控制器处理方法的返回值如果为 ModelAndView, 则其既包含视图信息,也包含模型数据信息。而且数据是放在请求域中。
- 添加模型数据
MoelAndView addObject(String attributeName, Object attributeValue) ModelAndView addAllObject(Map<String, ?> modelMap)
- 设置视图
void setView(View view) void setViewName(String viewName)
1.3 @SessionAttributes
- 若希望在多个请求之间共用某个模型属性数据,则可以在 控制器类(只能标记在类上) 上标注一个 @SessionAttributes,Spring MVC 将在模型(Map&Model&ModelMap&ModelAndView) 中对应的属性暂存到 HttpSession 中。
- @SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中。
@SessionAttributes(types=User.class) 会将隐含模型中所有类型为 User.class 的属性添加到会话中 @SessionAttributes(value={"user1", "user2"}) 会将隐含模型中属性名为 user1 或 user2 的属性添加到会话中 @SessionAttributes(types={User.class, Dept.class}) @SessionAttributes(value={"user1", "user2"}, types={Dept.class})
- 可能会引发异常:如果在处理类定义处标注了 @SessionAttributes("xxx"),则尝试从会话中获取该属性,并将其赋给该形参,然后再用请求消息填充该形参对象。如果在会话中找不到对应的属性,则抛出 HttpSessionRequiredException 异常。所以,还是使用原生 API 来存。
1.4 测试上述功能
@SessionAttributes(value= {"msg", "attr"}, types=String.class)
@Controller
public class OutputController {
@RequestMapping("/handle01")
public String handle01(Map<String, Object> map) {
// Map 类型:class org.springframework.validation.support.BindingAwareModelMap
System.out.println("Map 类型:" + map.getClass());
map.put("msg", "[Map] Can you hear me?");
map.put("attr", "val1");
return "success";
}
@RequestMapping("/handle02")
public String handle02(Model model) {
// Model 类型:class org.springframework.validation.support.BindingAwareModelMap
System.out.println("Model 类型:" + model.getClass());
model.addAttribute("msg", "[Model] Can you hear me?");
model.addAttribute("attr", "val2");
return "success";
}
@RequestMapping("/handle03")
public String handle03(ModelMap modelMap) {
// ModelMap 类型:class org.springframework.validation.support.BindingAwareModelMap
System.out.println("ModelMap 类型:" + modelMap.getClass());
modelMap.addAttribute("msg", "[ModelMap] Can you hear me?");
return "success";
}
@RequestMapping("/handle04")
public ModelAndView handle04() {
ModelAndView mav = new ModelAndView();
mav.addObject("msg", "[ModelAndView] Can you hear me?");
mav.setViewName("success");
return mav;
}
}
1.5 @ModelAttribute
1.5.1 使用情景
- 当修改 Book 对象时,SpringMVC 提供的要封装请求参数的 Book 对象不应该是自己 new 出来的,而应该是从 DB 中取出来的对象。
- 用这个准备好的对象封装请求参数,实现对这个对象的部分属性覆盖。
1.5.2 使用注解
- @ModelAttribute 注解可加在方法和参数上
- 在方法定义上使用该注解:Spring MVC 在调用目标处理方法前,会先逐个调用在方法级标注了 @ModelAttribute 的方法
- 在方法的形参前使用该注解:可以从隐含的模型数据中获取对象,再将请求参数绑定到对象中,再传入形参
- 应用于使用场景的步骤
- 将注解加在方法上,可以在提前运行的方法中去 DB 查 Book 的信息, 将这个 Book 信息保存起来(方便下一个方法还能接着使用)
- 提前方法的形参处声明一个 Map/Model/ModelMap,在其中存放 Book
- 在真正的处理方法处的 Book 形参上,也加上该注解。待该方法运行时,又会自动将之前查处的 Book 信息直接放入该形参
@RequestMapping("/updateBook")
public String updateBook(@RequestParam(value="author")String author, HttpServletRequest
request, Map<String, Object> model, @ModelAttribute("haha")Book book){
o2 = model;
b2 = book;
Object haha = model.get("haha");
// System.out.println("传入的Model:" + model.getClass()); // BindingAwareModelMap
System.out.println("o1==o2?" + (o1 == o2)); // true
System.out.println("b1==b2?" + (b1 == b2)+"-->" + (b2 == haha)); // true-->true
System.out.println("页面要提交过来的图书信息:" + book);
return "success";
}
@ModelAttribute
public void myModelAttribute(Map<String, Object> map){
Book book = new Book(1101, "狐狸在说什么", "韩", 98, 10, 98.98);
System.out.println("数据库中查到的图书信息是:" + book);
map.put("haha", book);
b1 = book;
o1 = map;
System.out.println("Map的类型:" + map.getClass()); // BindingAwareModelMap
}
2. 源码
2.1 doDispatcher 源码
- 适配器执行目标方法:L56 (源码对应 L945)
- 请求转发到对应视图:L74 (源码对应 L959)
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;
// 根据当前的请求 URI 找到目标处理器,拿到执行链
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response); // 找不到:404/throw
return;
}
// 拿到能执行目标处理器(类)所有方法的 [适配器]
// 适配器: 反射工具, 类型为 AnnotationMethodHandlerAdapter
// 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;
}
}
// 执行所有拦截器的 preHandle()
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler. [适配器] 执行目标方法,会将目标方法的返回值
// 作为视图名保存到 ModelAndView 对象的 view 属性中。注意:目标方法无论怎么写
// [适配器] 执行完成后都会将有关信息(请求域属性/视图)封装到 ModelAndView 中
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 封装的 View 信息将请求转发
// 到对应页面,还可以从请求域中获取其中封装的 ModelMap 数据)
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);
}
}
}
[小结] 请求过来,DispatcherServlet 收到请求,调用 doDispatcher() 进行处理:
getHandler()
根据当前请求地址找到能处理这个请求的类(处理器)
根据当前请求,在 HandlerMapping 中找到这个请求的映射信息,获取对应的目标处理器类。getHandlerAdapter()
根据当前处理器类获取到能执行这个处理器方法的适配器 ha
根据当前处理器类,找到 support 该处理器类的 HandlerAdapter(适配器)ha.handle()
使用适配器(AnnotationMethodHandlerAdapter) 执行目标方法,会返回一个 ModelAndView 对象processDispatchResult()
根据 ModelAndView 的信息转发到具体的视图,并可以在请求域中取出对应的模型数据
2.2 getHandler 细节
返回的是目标处理器的执行链:
getHandler(req) 方法源码:
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;
}
HandlerMapping 处理器映射;里面保存了每一个处理器能处理哪些请求。
2.3 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.4 SpringMVC 九大组件
SpringMVC 在工作的时候,关键位置都是由如下属性(组件) 来完成的,故称为 "SpringMVC的 9 大组件" (共同点:全是接口 → 接口就是规范,提供了扩展性)。
如下属性声明在 DispatcherServlet 中:
/** 文件上传解析器 */
private MultipartResolver multipartResolver;
/** 区域信息解析器,和国际化有关 */
private LocaleResolver localeResolver;
/** 主题解析器,支持主题效果更换 */
private ThemeResolver themeResolver;
/** Handler 映射信息 */
private List<HandlerMapping> handlerMappings;
/** Handler 适配器 */
private List<HandlerAdapter> handlerAdapters;
/** SpringMVC 异常解析器 */
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** 视图名转换,当处理器方法返回void,该解析器将视图名设置为请求URI */
private RequestToViewNameTranslator viewNameTranslator;
/** (FlashMap + Manager) SpringMVC 中允许重定向携带数据(放Session域)的功能 */
private FlashMapManager flashMapManager;
/** 视图解析器 */
private List<ViewResolver> viewResolvers;
handlerMappings 和 handlerAdapters 是什么时候有值的?
以初始化 HandlerMappings 为例:
2.5 handle 细节
2.5.1 要执行的目标方法
@RequestMapping("/hello")
public String hello(@RequestParam("bookName")String bookName
, Map<String, Object> map, HttpSession session
, @ModelAttribute("book") Book book) {
System.out.println("Hello~");
return "success";
}
@ModelAttribute
public void myModelAttribute(Map<String, Object> map) {
Book book = new Book(1101, "狐狸在说什么", "韩", 98, 10, 199.9);
System.out.println("数据库中查到的图书信息是:" + book);
map.put("book", book);
System.out.println("modelAttribute方法查询图书并保存到Map中:" + map.getClass());
}
2.5.2 AnnotationMethodHandlerAdapter
@Override
public ModelAndView handle(HttpServletRequest request
, HttpServletResponse response, Object handler) throws Exception {
// ...
return invokeHandlerMethod(request, response, handler);
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request
, HttpServletResponse response, Object handler) throws Exception {
// 拿到 [方法解析器]
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
// [方法解析器] 根据当前请求解析得到当前请求的目标处理方法
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
// 通过 [方法解析器] 来创建 [方法执行器]
ServletHandlerMethodInvoker methodInvoker
= new ServletHandlerMethodInvoker(methodResolver);
// 包装原生 request 和 response
ServletWebRequest webRequest = new ServletWebRequest(request, response);
// 创建本次请求的隐含模型!
ExtendedModelMap implicitModel = new BindingAwareModelMap();
// 真正执行目标方法:目标方法利用反射执行期间确定参数值,提前执行
// @ModelAttribute 标注的方法等所有操作都在其中,详见 2.5.3
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;
}
// 该方法被下面 HandlerMethodInvoker 在解析普通(没加注解的)参数时调用
@Override
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.5.3 HandlerMethodInvoker
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
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()) {
Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
// 来解析 @ModelAttribute 方法执行所需要的每一个参数的值(该方法还传入了隐含模型)
Object[] args = resolveHandlerArguments(attributeMethodToInvoke
, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
}
// 将方法上标注的 @ModelAttribute 的 value值取出来赋给 attrName,没设就给个空串""
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);
// 方法上标注的 @ModelAttribute 注解如果有 value 值,attrName = value的值。
// 如果没设置该属性(前面给了空串),attrName 就会变为返回值类型(resolvedType)
// 首字母小写,比如 void、book
if ("".equals(attrName)) {
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(
attributeMethodToInvoke, handler.getClass());
attrName = Conventions.getVariableNameForReturnType(
attributeMethodToInvoke, resolvedType, attrValue);
}
// 把提前运行的 @ModelAttribute 方法的返回值也放入隐含模型中!这是该注解标在方法上的
// 另一个作用:注解的 value 属性值为 key,以方法运行后的返回值为 value,放入到隐含
// 模型中,如 void=null。
if (!implicitModel.containsAttribute(attrName)) {
implicitModel.addAttribute(attrName, attrValue);
}
}
// 来解析目标方法执行所需要的每一个参数的值(该方法还传入了隐含模型)
Object[] args = resolveHandlerArguments(handlerMethodToInvoke
, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
}
ReflectionUtils.makeAccessible(handlerMethodToInvoke);
// 这里才是真正目标方法执行!
return handlerMethodToInvoke.invoke(handler, args);
}
// 如下方法,就确定目标方法运行时使用的每一个参数的值
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();
// 找到目标方法的 i 位置处参数的所有注解,如果有注解就解析并保存注解的信息
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(...);
}
// 没找到注解的情况
if (annotationsFound == 0) {
// 解析普通参数,底层实际调用 resolveStandardArgument(paramType
// , webRequest) 就是确定当前参数是否是 ServletAPI,详见 #2.5.2
Object argValue = resolveCommonArgument(methodParam, webRequest);
// Object UNRESOLVED = new Object();
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(...);
}
// 如果是,将隐含模型对象的引用赋值给该参数
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(...);
}
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) {
// 将请求参数中提交的每一个属性和(三步走中第 3 步创建的)JavaBean进行绑定
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;
}
// 确定自定义类型参数的值
private WebDataBinder resolveModelAttribute(String attrName, MethodParameter
methodParam, ExtendedModelMap implicitModel, NativeWebRequest
webRequest, Object handler) throws Exception {
// Bind request parameter onto object...
String name = attrName;
// 如果 attrName 是空串,就将参数类型的首字母小写作为值
if ("".equals(name)) {
name = Conventions.getVariableNameForParameter(methodParam);
}
Class<?> paramType = methodParam.getParameterType();
// SpringMVC 确定 POJO 的 3 步
Object bindObject;
// 1) 如果隐含模型中有这个 key (标了 @ModelAttribute 就是注解
// 指定的 value,没标就是参数类型首字母小写)
if (implicitModel.containsKey(name)) {
bindObject = implicitModel.get(name);
}
// 2) 如果是 @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");
}
}
// 3) 如果都不是,就利用反射创建对象
else {
bindObject = BeanUtils.instantiateClass(paramType);
}
WebDataBinder binder = createBinder(webRequest, bindObject, name);
initBinder(handler, name, binder, webRequest);
return binder;
}