springMVC源码分析--ModelFactory
ModelFactory是用来维护Model的,具体包含两个功能
(1)初始化Model
(2)处理器执行后将Model中相应的参数更新到SessionAttributes中
1、初始化Model其实是对@ModelAttribute和@SessionAttribute注解的执行,执行的操作是在initModel中,包括获取@SessionAttribute注解的参数的值以及被@ModelAttribute注解的函数。
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod) throws Exception { //从注解了@SessionAttribute中取出保存的参数,并合并到mavContainer中 Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); mavContainer.mergeAttributes(sessionAttributes); //执行注释了@ModelAttribute的方法并将结果设置到Model invokeModelAttributeMethods(request, mavContainer); //遍历既注释了@ModelAttribute又在@SessionAttribute注释中的参数 for (String name : findSessionAttributeArguments(handlerMethod)) { if (!mavContainer.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'"); } mavContainer.addAttribute(name, value); } } }
2、更新Model的操作是在updateModel中,首先会更新@SessionAttribute中注解的值,然后会更新Model中的值。
//更新Model,包括两部分一是修改@SessionAttribute注解中值的value,二是更新Model中的值 public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception { ModelMap defaultModel = mavContainer.getDefaultModel(); if (mavContainer.getSessionStatus().isComplete()){ this.sessionAttributesHandler.cleanupAttributes(request); } else { this.sessionAttributesHandler.storeAttributes(request, defaultModel); } if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) { updateBindingResult(request, defaultModel); } }
ModelFactory的完整源码如下:
public final class ModelFactory { private static final Log logger = LogFactory.getLog(ModelFactory.class); private final List<ModelMethod> modelMethods = new ArrayList<ModelMethod>(); private final WebDataBinderFactory dataBinderFactory; private final SessionAttributesHandler sessionAttributesHandler; public ModelFactory(List<InvocableHandlerMethod> invocableMethods, WebDataBinderFactory dataBinderFactory, SessionAttributesHandler sessionAttributesHandler) { if (invocableMethods != null) { for (InvocableHandlerMethod method : invocableMethods) { this.modelMethods.add(new ModelMethod(method)); } } this.dataBinderFactory = dataBinderFactory; this.sessionAttributesHandler = sessionAttributesHandler; } public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod) throws Exception { //从注解了@SessionAttribute中取出保存的参数,并合并到mavContainer中 Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request); mavContainer.mergeAttributes(sessionAttributes); //执行注释了@ModelAttribute的方法并将结果设置到Model invokeModelAttributeMethods(request, mavContainer); //遍历既注释了@ModelAttribute又在@SessionAttribute注释中的参数 for (String name : findSessionAttributeArguments(handlerMethod)) { if (!mavContainer.containsAttribute(name)) { Object value = this.sessionAttributesHandler.retrieveAttribute(request, name); if (value == null) { throw new HttpSessionRequiredException("Expected session attribute '" + name + "'"); } mavContainer.addAttribute(name, value); } } } private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception { while (!this.modelMethods.isEmpty()) { //获取注释了@ModelAttribute的方法 InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod(); //获取注释了@ModelAttribute中设置的value作为参数名 String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value(); //如果参数名已经在mavContainer中则跳过 if (mavContainer.containsAttribute(modelName)) { continue; } //执行@ModelAttribute注释的方法 Object returnValue = attrMethod.invokeForRequest(request, mavContainer); if (!attrMethod.isVoid()){ //使用getNameForReturnValue获取参数名 String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType()); if (!mavContainer.containsAttribute(returnValueName)) { mavContainer.addAttribute(returnValueName, returnValue); } } } } //获取每次要处理的ModelMethod private ModelMethod getNextModelMethod(ModelAndViewContainer mavContainer) { for (ModelMethod modelMethod : this.modelMethods) { if (modelMethod.checkDependencies(mavContainer)) { if (logger.isTraceEnabled()) { logger.trace("Selected @ModelAttribute method " + modelMethod); } this.modelMethods.remove(modelMethod); return modelMethod; } } ModelMethod modelMethod = this.modelMethods.get(0); if (logger.isTraceEnabled()) { logger.trace("Selected @ModelAttribute method (not present: " + modelMethod.getUnresolvedDependencies(mavContainer)+ ") " + modelMethod); } this.modelMethods.remove(modelMethod); return modelMethod; } //查找被@SessionAttribute注解的并且被@ModelAttribute注解的参数 private List<String> findSessionAttributeArguments(HandlerMethod handlerMethod) { List<String> result = new ArrayList<String>(); for (MethodParameter parameter : handlerMethod.getMethodParameters()) { if (parameter.hasParameterAnnotation(ModelAttribute.class)) { String name = getNameForParameter(parameter); //判断是否@SessionAttribute注解中的值 if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) { result.add(name); } } } return result; } //获取参数名称 public static String getNameForParameter(MethodParameter parameter) { ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class); String attrName = (annot != null) ? annot.value() : null; return StringUtils.hasText(attrName) ? attrName : Conventions.getVariableNameForParameter(parameter); } //获取@ModelAttribute注解中的值 public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) { ModelAttribute annotation = returnType.getMethodAnnotation(ModelAttribute.class); if (annotation != null && StringUtils.hasText(annotation.value())) { return annotation.value(); } else { Method method = returnType.getMethod(); Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getContainingClass()); return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue); } } //更新Model,包括两部分一是修改@SessionAttribute注解中值的value,二是更新Model中的值 public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception { ModelMap defaultModel = mavContainer.getDefaultModel(); if (mavContainer.getSessionStatus().isComplete()){ this.sessionAttributesHandler.cleanupAttributes(request); } else { this.sessionAttributesHandler.storeAttributes(request, defaultModel); } if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) { updateBindingResult(request, defaultModel); } } // private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception { List<String> keyNames = new ArrayList<String>(model.keySet()); for (String name : keyNames) { Object value = model.get(name); if (isBindingCandidate(name, value)) { String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name; if (!model.containsAttribute(bindingResultKey)) { WebDataBinder dataBinder = dataBinderFactory.createBinder(request, value, name); model.put(bindingResultKey, dataBinder.getBindingResult()); } } } } // private boolean isBindingCandidate(String attributeName, Object value) { if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) { return false; } Class<?> attrType = (value != null) ? value.getClass() : null; if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, attrType)) { return true; } return (value != null && !value.getClass().isArray() && !(value instanceof Collection) && !(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass())); } private static class ModelMethod { private final InvocableHandlerMethod handlerMethod; private final Set<String> dependencies = new HashSet<String>(); private ModelMethod(InvocableHandlerMethod handlerMethod) { this.handlerMethod = handlerMethod; for (MethodParameter parameter : handlerMethod.getMethodParameters()) { if (parameter.hasParameterAnnotation(ModelAttribute.class)) { this.dependencies.add(getNameForParameter(parameter)); } } } public InvocableHandlerMethod getHandlerMethod() { return this.handlerMethod; } public boolean checkDependencies(ModelAndViewContainer mavContainer) { for (String name : this.dependencies) { if (!mavContainer.containsAttribute(name)) { return false; } } return true; } public List<String> getUnresolvedDependencies(ModelAndViewContainer mavContainer) { List<String> result = new ArrayList<String>(this.dependencies.size()); for (String name : this.dependencies) { if (!mavContainer.containsAttribute(name)) { result.add(name); } } return result; } @Override public String toString() { return this.handlerMethod.getMethod().toGenericString(); } } }
springMVC源码分析--ModelFactory的更多相关文章
- 8、SpringMVC源码分析(3):分析ModelAndView的形成过程
首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throw ...
- 7、SpringMVC源码分析(2):分析HandlerAdapter.handle方法,了解handler方法的调用细节以及@ModelAttribute注解
从上一篇 SpringMVC源码分析(1) 中我们了解到在DispatcherServlet.doDispatch方法中会通过 mv = ha.handle(processedRequest, res ...
- springMVC源码分析--访问请求执行ServletInvocableHandlerMethod和InvocableHandlerMethod
在之前一篇博客中springMVC源码分析--RequestMappingHandlerAdapter(五)我们已经简单的介绍到具体请求访问的执行某个Controller中的方法是在RequestMa ...
- springMVC源码分析--RequestMappingHandlerAdapter(五)
上一篇博客springMVC源码分析--HandlerAdapter(一)中我们主要介绍了一下HandlerAdapter接口相关的内容,实现类及其在DispatcherServlet中执行的顺序,接 ...
- 框架-springmvc源码分析(一)
框架-springmvc源码分析(一) 参考: http://www.cnblogs.com/heavenyes/p/3905844.html#a1 https://www.cnblogs.com/B ...
- [心得体会]SpringMVC源码分析
1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...
- springMVC源码分析--ViewNameMethodReturnValueHandler返回值处理器(三)
之前两篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)和springMVC源码分析--HandlerMethodReturnValu ...
- springMVC源码分析--HandlerMethodReturnValueHandlerComposite返回值解析器集合(二)
在上一篇博客springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一)我们介绍了返回值解析器HandlerMethodReturnValueHand ...
- springMVC源码分析--RequestParamMethodArgumentResolver参数解析器(三)
之前两篇博客springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一)和springMVC源码解析--HandlerMethodArgumentResol ...
随机推荐
- 使用MFC创建C++程序
编译环境:VS2017 MFC简介: MFC(MicrosoftFoundationClasses)是微软基础类库的简称,是微软公司实现的一个c++类库,主要封装了大部分的windows API函数. ...
- JavaScript push() 方法
定义和用法: push() :可向数组的末尾添加一个或多个元素,并返回新的长度. 语法 arrayObject.push(newelement1,newelement2,....,newelement ...
- pymysql实现从a表过滤出有效信息添加至b表
# Author: yeshengbao # -- coding: utf-8 -- # @Time : 2018/4/16 19:23 import pymysql # 创建连接 conn = py ...
- 6.19 noip模拟题(题目及解析转自 hzwer 2014-3-15 NOIP模拟赛)
Problem 1 高级打字机(type.cpp/c/pas) [题目描述] 早苗入手了最新的高级打字机.最新款自然有着与以往不同的功能,那就是它具备撤销功能,厉害吧. 请为这种高级打字机设计一个程序 ...
- [SHOI2014]概率充电器
Description 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器: “采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器, ...
- HDU 1729
给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,gloria想从迷宫的一个位置走到另外一个位置 她在行走过程中,不能转太多弯了,否则她会晕倒的. (每次在一个方向上一直走到底,并push ...
- jquery 跨域请求数据问题
昨天参加了一个前端的面试,被问到一个跨域请求数据问题,我们之前一直用的是apicloud的api进行请求的,跨域是被apicloud封装起来的,也就没有注意跨域请求数据的问题.当被问到用jquery跨 ...
- Mysql锁机制--并发事务带来的更新丢失问题
Mysql 系列文章主页 =============== 刚开始学习 Mysql 锁的时候,觉得 Mysql 使用的是行锁,再加上其默认的可重复读的隔离级别,那就应该能够自动解决并发事务更新的问题.可 ...
- JavaScript 题目(作用域)
var length = 10 function fn(){ alert(this.length) } var obj = { length: 5, method: function(fn) { fn ...
- Mysql中where条件一个单引号引发的性能损耗
日常写SQL中可能会有一些小细节忽略了导致整个sql的性能下降了好几倍甚至几十倍,几百倍.以下这个示例就是mysql语句中的一个单引号('')引发的性能耗损,我相信很多朋友都遇到过,甚至还在这样写. ...