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 ...
随机推荐
- PIL绘图
# coding:utf-8 # PIL的ImageDraw 提供了一系列绘图方法,让我们可以直接绘图.比如要生成字母验证码图片 from PIL import Image, ImageDraw, I ...
- xpath的一般用法与特殊用法
# xpath的使用 安装lxml from lxml import etree Selector = etree.HTML(网页代码) Selector.xpath(一段神奇的代码) xpath的一 ...
- 基于 MySQL 的数据库实践(基本查询)
首先根据准备工作中的操作导入大学模式,打开数据库连接后进入到 MySQL 的交互界面,再使用命令 use db-book; 切换到 db-book 数据库. 单关系查询 SQL 查询的基本结构由三个子 ...
- EFCore CodeFirst 连接MySql
一.工具及环境 Visual Studio 2017 15.4.3 MySql Navicat for MySQL 二.Entity Framwork Core 2.0 MySql Code Firs ...
- [TJOI 2013]单词
Description 题库链接 给出一篇文章的所有单词,询问每个单词出现的次数. 单词总长 \(\leq 10^6\) Solution 算是 \(AC\) 自动机的板子,注意拼成文章的时候要在单词 ...
- [HNOI 2001]矩阵乘积
Description Input Output Sample Input 1 2 3 4 2 3 1 1 3 1 4 5 2 2 1 3 1 2 1 2 2 2 1 1 3 1 2 3 2 4 1 ...
- [Luogu 2817]宋荣子的城堡
Description saruka有一座大大的城堡!城堡里面有n个房间,每个房间上面都写着一个数字p[i].有一天,saruka邀请他的小伙伴LYL和 MagHSK来城堡里玩耍(为什么没有妹子),他 ...
- [IOI2007]训练路径
Description 马克(Mirko)和斯拉夫克(Slavko)正在为克罗地亚举办的每年一次的双人骑车马拉松赛而紧张训练.他们需要选择一条训练路径. 他们国家有N个城市和M条道路.每条道路连接两个 ...
- 【BZOJ 2395】Time is money
题目大意有n个城市(编号从0..n-1),m条公路(双向的),从中选择n-1条边,使得任意的两个城市能够连通,一条边需要的c的费用和t的时间,定义一个方案的权值v=n-1条边的费用和*n-1条边的时间 ...
- Codeforces 812E Sagheer and Apple Tree
大致题意: 给你一颗树,这个树有下列特征:每个节点上有若干个苹果,且从根节点到任意叶子节点的路径长度奇偶性相同. 甲和乙玩(闲)游(得)戏(慌). 游戏过程中,甲乙轮流将任意一个节点的若干个苹果移向它 ...