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 ...
随机推荐
- 前端之旅HTML与CSS篇之清除浮动塌陷
以下内容为转载. 方法1:给浮动的元素的上级添加高度如果一个元素要浮动,那么它的祖先元素一定要有高度.高度的盒子,才能关住浮动.只要浮动在一个有高度的盒子中,那么这个浮动就不会影响后面的浮动元素.所以 ...
- [LeetCode] Longest Palindromic Subsequence 最长回文子序列
Given a string s, find the longest palindromic subsequence's length in s. You may assume that the ma ...
- 前端监控系统(二)JS错误日志收集篇
前端监控系统 目前已经上线,欢迎使用! 服务器搭建好了,可以着手开发了. 其实前端需要分析的数据有很多,包括,PVUV, 接口请求统计,耗时统计,JS错误统计,用户使用设备统计,用户地域分布,页面用户 ...
- shell编程-项目部署(二)
上节我们讲了项目部署的准备工作,现在具体讲下代码部署 首先梳理下思路,大致是这样: 获取代码 打包代码 传输代码 关闭应用 解压文件 放置文件(备份老文件,放置新的文件) 开启应用 最后检查下 OK, ...
- Discuz! 7.x 反射型xss
引用:http://wooyun.jozxing.cc/static/bugs/wooyun-2014-084097.html 在/include/global.func.php 文件中 第1036- ...
- [Luogu 1730]最小密度路径
Description 给出一张有N个点M条边的加权有向无环图,接下来有Q个询问,每个询问包括2个节点X和Y,要求算出从X到Y的一条路径,使得密度最小(密度的定义为,路径上边的权值和除以边的数量). ...
- ●BZOJ 3640 JC的小苹果
题链: http://www.lydsy.com/JudgeOnline/problem.php?id=3640题解: 期望dp,高斯消元 设dp[i][h]在i位置且血量为h这个状态的期望经过次数. ...
- HDU 1729
给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,gloria想从迷宫的一个位置走到另外一个位置 她在行走过程中,不能转太多弯了,否则她会晕倒的. (每次在一个方向上一直走到底,并push ...
- Hdu 5595 GTW likes math
题意: 问题描述 某一天,GTW听了数学特级教师金龙鱼的课之后,开始做数学<从自主招生到竞赛>.然而书里的题目太多了,GTW还有很多事情要忙(比如把妹),于是他把那些题目交给了你.每一道题 ...
- bzoj2243[SDOI2011]染色 树链剖分+线段树
2243: [SDOI2011]染色 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 9012 Solved: 3375[Submit][Status ...