博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringMVC源码解析- HandlerAdapter - ModelFactory
阅读量:6587 次
发布时间:2019-06-24

本文共 15999 字,大约阅读时间需要 53 分钟。

ModelFactory主要是两个职责:

  1. 初始化model

  2. 处理器执行后将modle中相应参数设置到SessionAttributes中

 

我们来看看具体的处理逻辑(直接充当分析目录):

1. 初始化model

  1.1 解析类上使用的sessionAttributres,将获取参数合并到mavContainer中

  1.2 执行注解了@ModelAttribute的方法,并将结果同步到Model

    参数名的生成规则:@ModelAttribute中定义的value > 方法的返回类型决定(直接往model.addAttribute的除外)

  1.3 将注解@ModelAttribute方法参数(在@SessionAttributes定义范围内)同步到model中

     将方法中使用@ModelAttribute的参数跟@SessionAttribute核对,如果都定义了,需要将其参数值同步至mavContainer

2. 处理器执行后将modle中相应参数设置到SessionAttributes中

  2.1 如果SessionStatus被调用了setComplete则清除sesssionAttributesHandler中缓存的数据

  2.2 如果没清除,将model中的数据同步至sessionAttributesHandler中

  2.3 如果handler还没处理完(是否需要渲染页面),绑定BindingResult到model(如果需要的话)

  上面的代码说明在日常开发时,SessionStatus.setComplete写在方法哪个位置都行,因为他是在方法执行后才在这边调用,跟方法中的顺序无关.

 

 1. 初始化model

做了三个事情,详细见源码中的注释吧:

1 package org.springframework.web.method.annotation; 2 public final class ModelFactory { 3     public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod) 4             throws Exception { 5     // 获取使用@SessionAttributes注解并已经解析的参数,合并到mavContainer 6         Map
attributesInSession = this.sessionAttributesHandler.retrieveAttributes(request); 7 mavContainer.mergeAttributes(attributesInSession); 8 // 执行使用@ModelAttribute注解的方法,并将结果设置到mavContainer 9 invokeModelAttributeMethods(request, mavContainer);10 // 将同时使用@ModelAttribute和@SessionAttributes注解的参数设置到mavContainer11 for (String name : findSessionAttributeArguments(handlerMethod)) {12 if (!mavContainer.containsAttribute(name)) {13 Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);14 if (value == null) {15 throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");16 }17 mavContainer.addAttribute(name, value);18 }19 }20 }21 // ...22 }

 

1.1 解析类上使用的sessionAttributres,将获取参数合并到mavContainer中

这部分,之前的<>已经讲述得很细,这边就不展开.

 

1.2 执行注解了@ModelAttribute的方法,并将结果同步到Model

  迭代所有使用@ModelAttribute注解的方法

  获取@ModelAttribute中的value属性值作为 model attribute,如果mavContainer中已经存在则退出

  委托InvocableHandlerMethod的invokeForRequest生成属性值.

    a,获取当前方法的调用参数

    b,直接执行invoke,并返回结果

  如果方法不是void的,则需要将值同步到mavContainer

    a,如果方法是void,则说明用户直接将参数通过model.addAttribute设置好值了

    b,参数名的生成规则:@ModelAttribute中定义的value > 方法的返回类型决定

    根据方法的返回类型决定参数名时,大致的规则如下:

      String -> string(这边就解释我之前没搞明白)

      List<Double> -> doubleList

    c,如果mavContainer中还没有这个参数值,则同步进去

1 package org.springframework.web.method.annotation; 2 public final class ModelFactory { 3     private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer) 4             throws Exception { 5         // 迭代使用@ModelAttribute注解的方法 6         for (InvocableHandlerMethod attrMethod : this.attributeMethods) { 7             // 使用@ModelAttribute的value值作为 attribute name 8             String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value(); 9             if (mavContainer.containsAttribute(modelName)) {10                 continue;11             }12             // 委托InvocableHandlerMethod调用方法,生成值13             Object returnValue = attrMethod.invokeForRequest(request, mavContainer);14             // 如果方法返回值,需要将这个值同步到mavContainer中15             if (!attrMethod.isVoid()){16                 // 生成参数名:注解的value或者返回值类型17                 String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());18                 if (!mavContainer.containsAttribute(returnValueName)) {19                     mavContainer.addAttribute(returnValueName, returnValue);20                 }21             }22         }23     }24     // ...25 }

 

看看InvocableHandlerMethod的invokeForRequest(NativeWebRequest request,ModelAndViewContainer mavContainer,Object... providedArgs)

  这边涉及到两个封装类:InvocableHandlerMethod和MethodParameter.

  InvocableHandlerMethod封装一个可执行的方法,在HandlerMethod基础上添加方法参数解析的职责.

  MethodParameter封装方法定义相关的概念

具体的处理逻辑还是看代码中的注释吧.

1 package org.springframework.web.method.support; 2 public class InvocableHandlerMethod extends HandlerMethod { 3     public final Object invokeForRequest(NativeWebRequest request, 4                                          ModelAndViewContainer mavContainer, 5                                          Object... providedArgs) throws Exception { 6         // 生成方法调用时的参数 7         Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); 8         // 霸气的调用 9         Object returnValue = invoke(args);10 11         return returnValue;12     }13     private Object[] getMethodArgumentValues(14             NativeWebRequest request, ModelAndViewContainer mavContainer,15             Object... providedArgs) throws Exception {16         // 获取参数,这边没有值17         MethodParameter[] parameters = getMethodParameters();18         Object[] args = new Object[parameters.length];19         for (int i = 0; i < parameters.length; i++) {20             MethodParameter parameter = parameters[i];21             // 参数名称查找器,反射中拿不到参数名,所以使用spring的parameterNameDiscover22             parameter.initParameterNameDiscovery(parameterNameDiscoverer);23             // 获取参数的目标类型,methodParam.setParameterType(result);设置.这边具体的逻辑后面再细化24             GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());25             // 尝试通过类型判断,获取参数的值26             args[i] = resolveProvidedArgument(parameter, providedArgs);27             if (args[i] != null) {28                 continue;29             }30             // 使用HandlerMethodArgumentResolver,判断是否支持处理31             if (argumentResolvers.supportsParameter(parameter)) {32                 try {33                     // 这边直接处理,实际执行时,是通过责任链设计模式处理34                     args[i] = argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory);35                     continue;36                 } catch (Exception ex) {37                     throw ex;38                 }39             }40 41             if (args[i] == null) {42                 String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);43                 throw new IllegalStateException(msg);44             }45         }46         return args;47     }48     private Object invoke(Object... args) throws Exception {49         // 解决权限的问题50         ReflectionUtils.makeAccessible(this.getBridgedMethod());51         try {52             return getBridgedMethod().invoke(getBean(), args);53         }54         catch (IllegalArgumentException | InvocationTargetExceptione) {55             // 省略异常处理机制56         }57     }58     // ...59 }

我们再来看看参数名称的生成规则吧:

  如果@ModelAttribute中定义了value,就以value命名

  如果注解中没有定义value,则根据返回值类型定义名称

  如:String会被定义为string,List<Double>会被定义为doubleList(集合都是这样定义的,包括array数组)

1 package org.springframework.web.method.annotation; 2 public final class ModelFactory { 3  4     public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) { 5         ModelAttribute annot = returnType.getMethodAnnotation(ModelAttribute.class); 6         if (annot != null && StringUtils.hasText(annot.value())) { // 注解中定义了value 7             return annot.value(); 8         } 9         else { // 根据类型生成10             Method method = returnType.getMethod();11             Class
resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getDeclaringClass());12 return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);13 }14 }15 // ...16 }

接下来是如何根据返回值类型生成参数名称的逻辑,挺有意思,重点展开:

这边又根据方法的signature中定义的参数类型是否细化再衍生一个分支:

  如果方法签名中只定义Object类型,则需要根据value生成;否则根据签名生成

1 package org.springframework.core; 2 public abstract class Conventions { 3     public static String getVariableNameForReturnType(Method method, Class resolvedType, Object value) { 4         // 如果signature定义为object,则根据value来判断 5         if (Object.class.equals(resolvedType)) { 6             if (value == null) { 7                 throw new IllegalArgumentException("Cannot generate variable name for an Object return type with null value"); 8             } 9             // 这边的处理逻辑跟下面的很类似,不展开.差别是一个根据value,一个根据resolvedType判断10             return getVariableName(value);11         }12 13         Class valueClass;14         // 是否是数组或集合15         boolean pluralize = false;16 17         if (resolvedType.isArray()) { // 数组,读取内部元素的类型18             valueClass = resolvedType.getComponentType();19             pluralize = true;20         }21         else if (Collection.class.isAssignableFrom(resolvedType)) { // 集合22             // 集合内的元素类型23             valueClass = GenericCollectionTypeResolver.getCollectionReturnType(method);24             if (valueClass == null) {25                 if (!(value instanceof Collection)) {
// 跟value再校验一遍类型26 throw new IllegalArgumentException(27 "Cannot generate variable name for non-typed Collection return type and a non-Collection value");28 }29 Collection collection = (Collection) value;30 if (collection.isEmpty()) {31 throw new IllegalArgumentException(32 "Cannot generate variable name for non-typed Collection return type and an empty Collection value");33 }34 // 获取集合中的第一个value35 Object valueToCheck = peekAhead(collection);36 // 获取value的类系37 valueClass = getClassForValue(valueToCheck);38 }39 pluralize = true;40 }41 else {42 valueClass = resolvedType;43 }44 45 String name = ClassUtils.getShortNameAsProperty(valueClass);46 return (pluralize ? pluralize(name) : name);47 }48 // 获取集合中的第一个value49 private static Object peekAhead(Collection collection) {50 Iterator it = collection.iterator();51 if (!it.hasNext()) {52 throw new IllegalStateException(53 "Unable to peek ahead in non-empty collection - no element found");54 }55 Object value = it.next();56 if (value == null) {57 throw new IllegalStateException(58 "Unable to peek ahead in non-empty collection - only null element found");59 }60 return value;61 }62 private static Class getClassForValue(Object value) {63 Class valueClass = value.getClass();64 // 代理时根据接口获取,遍历时以第一个符合条件的为准65 if (Proxy.isProxyClass(valueClass)) {66 Class[] ifcs = valueClass.getInterfaces();67 for (Class ifc : ifcs) {68 if (!ignoredInterfaces.contains(ifc)) {69 return ifc;70 }71 }72 }73 else if (valueClass.getName().lastIndexOf('$') != -1 && valueClass.getDeclaringClass() == null) {74 // '$' in the class name but no inner class -75 // assuming it's a special subclass (e.g. by OpenJPA)76 valueClass = valueClass.getSuperclass();77 }78 return valueClass;79 }80 // 数组或结合统一添加后缀List81 private static String pluralize(String name) {82 //private static final String PLURAL_SUFFIX = "List";83 return name + PLURAL_SUFFIX;84 }85 86 }

 

1.3 将注解@ModelAttribute方法参数(在@SessionAttributes定义范围内)同步到model中

遍历HandlerMethod的所有参数,找出使用了@ModelAttribute注解的参数

  获取参数的名称:注解value值 > 参数类型

  核对这个参数名称是否在@SessionAttributes注解内

  如果mavContainer中还没有该参数,继续处理

  获取缓存在sessionAttributesHandler中的参数值

  如果值为空,抛HttpSessionRequiredException

  否则同步到mavContainer中

1 package org.springframework.web.method.annotation; 2 public final class ModelFactory { 3     // ... 4     public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod) 5             throws Exception { 6         // ... 7         for (String name : findSessionAttributeArguments(handlerMethod)) { 8             if (!mavContainer.containsAttribute(name)) { 9                 Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);10                 if (value == null) {11                     throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");12                 }13                 mavContainer.addAttribute(name, value);14             }15         }16     }17     private List
findSessionAttributeArguments(HandlerMethod handlerMethod) {18 List
result = new ArrayList
();19 // 这边找的是HandlerMethod的参数20 for (MethodParameter param : handlerMethod.getMethodParameters()) {21 if (param.hasParameterAnnotation(ModelAttribute.class)) {22 String name = getNameForParameter(param);23 if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, param.getParameterType())) {24 result.add(name);25 }26 }27 }28 return result;29 }30 public static String getNameForParameter(MethodParameter parameter) {31 ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);32 String attrName = (annot != null) ? annot.value() : null;33 // 如果value为空,获取参数类型解析属性名称34 return StringUtils.hasText(attrName) ? attrName : Conventions.getVariableNameForParameter(parameter);35 }36 }

 

2. 处理器执行后将modle中相应参数设置到SessionAttributes中

  2.1 如果SessionStatus被调用了setComplete则清除sesssionAttributesHandler中缓存的数据

  2.2 如果没清除,将model中的数据同步至sessionAttributesHandler中

  2.3 如果handler还没处理完(是否需要渲染页面),绑定BindingResult到model(如果需要的话)

 

还需要补充说明的是:

判断绑定BindingResult到model时的条件(满足任意):

  a,不是其他参数绑定结果的Bindingresult

  b,@SessionAttributes注解定义范围内

  c, 不是null,数组,集合,map,简单数据类型

剩下的看代码注释就行了

1 package org.springframework.web.method.annotation; 2 public final class ModelFactory { 3     // ... 4     public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception { 5         if (mavContainer.getSessionStatus().isComplete()){ // 清除 6             this.sessionAttributesHandler.cleanupAttributes(request); 7         } 8         else { // 不清除,那么就需要同步 9             this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());10         }11 12         if (!mavContainer.isRequestHandled()) {13             updateBindingResult(request, mavContainer.getModel());14         }15     }16 17     private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {18         List
keyNames = new ArrayList
(model.keySet());19 for (String name : keyNames) {20 Object value = model.get(name);21 // 核对是否需要绑定BindingResult到model22 if (isBindingCandidate(name, value)) {23 String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;24 25 if (!model.containsAttribute(bindingResultKey)) { // 不是其他参数绑定的结果26 WebDataBinder dataBinder = binderFactory.createBinder(request, value, name);27 model.put(bindingResultKey, dataBinder.getBindingResult());28 }29 }30 }31 }32 33 private boolean isBindingCandidate(String attributeName, Object value) {34 // 不是其他参数绑定的结果35 if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {36 return false;37 }38 // 是否在@SessionAttributes注解定义中39 Class
attrType = (value != null) ? value.getClass() : null;40 if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, attrType)) {41 return true;42 }43 // 不是null,数组,集合,map,简单数据类型,则调用44 return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&45 !(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));46 }47 }

 

转载于:https://www.cnblogs.com/leftthen/p/5224816.html

你可能感兴趣的文章
ActionScript 3.0游戏编程——创建简单的ActionScript程序
查看>>
函数const
查看>>
关于“Return empty arrays or collections, not nulls”的思考
查看>>
CodeForces-1167E-Range Deleting
查看>>
兼容多个版本程序集的web.config配置
查看>>
java finally块执行时机分析
查看>>
day6 字符串
查看>>
JMeter5.0 边界提取器使用
查看>>
Windows Azure 上的 Symfony,适用于 PHP 开发者的强大组合
查看>>
堆和栈的区别 (转贴)
查看>>
通过包名获取该包下的所有类
查看>>
【JavaScript学习笔记】画图
查看>>
Linux写时拷贝技术(copy-on-write)
查看>>
opencv视频读取问题
查看>>
java Iterator Fail-fast机制
查看>>
Java堆外内存之五:堆外内存管理类ByteBuffer
查看>>
HTML5 input placeholder 颜色修改
查看>>
TJ/T808 终端通讯协议设计与实现(码农本色)
查看>>
分布式搜索引擎Elasticsearch的查询与过滤
查看>>
SolidEdge 工程图中如何给零件添加纹理或贴图
查看>>