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 ...
随机推荐
- html5之一些通用属性
dir属性:定义元素内容排序方式(rtl,ltr,auto)contentEditable属性:内容是否可编辑tabindex属性:按tab键激活元素 <!DOCTYPE html>< ...
- NSURLSession使用, 后台下载
现在越来越多的开发习惯于使用各种第三方框架,诚然,第三方框架给我们开发带来了很多便利,但我们不能太依赖于第三方,在使用第三方的同时学习其原理才是硬道理. 所以今天我们就来讲讲AFNetworking所 ...
- [LeetCode] 4 Keys Keyboard 四键的键盘
Imagine you have a special keyboard with the following keys: Key 1: (A): Print one 'A' on screen. Ke ...
- [LeetCode] Teemo Attacking 提莫攻击
In LLP world, there is a hero called Teemo and his attacking can make his enemy Ashe be in poisoned ...
- hadoop一键安装伪分布式
hadoop伪分布式和hive在openSUSE中的安装 在git上的路径为:https://github.com/huabingood/hadoop--------/tree/master 各个文件 ...
- HTML5 AJAX跨域请求
HTML5新的标准中,增加了" Cross-Origin Resource Sharing"特性,这个特性的出现使得跨域通信只需通过配置http协议头来即可解决. Cross-Or ...
- 习题10-1 UVA 11040(无聊水一水)
题意: 给你一个残缺的塔,每个数字由他下面左右两个数相加得.给你其中一部分,要求输出全部的数字. #include <iostream> #include <cstdio> # ...
- 12_Python的(匿名函数)Lambda表达式_Python编程之路
Python作为一门高级语言,与很多编程语言一样都具有匿名函数这一特征 匿名函数,也就Lambda表达式,通俗来讲就是不用命名的方法,直接定义,直接用即可 创建匿名函数需要用到Lambda关键字,下面 ...
- sprintf()、fprintf()、fscanf()的用法
sprintf函数的用法1.该函数包含在stdio.h的头文件中. 2.sprintf和平时我们常用的printf函数的功能很相似.sprintf函数打印到字符串中,而printf函数打印输出到屏幕上 ...
- Linux下如何进入中文目录
给Linux安装图形用户界面之后,会在工作目录中生成图片, 文档, 下载........等中文目录,以前不知道如何进入这些目录,感觉也没有必要,今天在火狐上下载了一个软件,默认在下载这个目录当中,实在 ...