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 ...
随机推荐
- Spring学习(1)——快速入门
认识 Spring 框架 Spring 框架是 Java 应用最广的框架,它的成功来源于理念,而不是技术本身,它的理念包括 IoC (Inversion of Control,控制反转) 和 AOP( ...
- java模式之模板模式——抽象类
模板设计模式(Template ) abstract class Action{ // 定义一个行为类 public static final String WORK = "work&quo ...
- [Shell]Linux 下 Shell 的自动交互
在编写脚本的时候经常会遇到这种情况,某些程序的命令执行的之后可能会要求用户进行输入,这个时候就需要一些特殊写法来应对这种问题了.这里参考 这篇文章提到可以使用 delimiter 分界符来解决. 也就 ...
- TensorFlow学习笔记(MNIST报错修正 适用Tensorflow1.3)
在Tensorflow实战Google框架下的深度学习这本书的MNIST的图像识别例子中,每次都要报错 错误如下: Only call `sparse_softmax_cross_entropy_ ...
- [HNOI 2015]开店
Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...
- [HNOI 2011]XOR和路径
Description 给定一个无向连通图,其节点编号为 1 到 N,其边的权值为非负整数.试求出一条从 1 号节点到 N 号节点的路径,使得该路径上经过的边的权值的“XOR 和”最大.该路径可以重复 ...
- AtCoder Grand Contest 021 D - Reversed LCS
Description Takahashi has decided to give a string to his mother. The value of a string T is the len ...
- ●CodeForces 549F Yura and Developers
题链: http://codeforces.com/problemset/problem/549/F题解: 分治,链表. 考虑对于一个区间[L,R],其最大值在p位置, 那么答案的贡献就可以分为3部分 ...
- 12563 Jin Ge Jin Qu hao
• Don’t sing a song more than once (including Jin Ge Jin Qu). • For each song of length t, either si ...
- 2015 多校联赛 ——HDU5323(搜索)
Solve this interesting problem Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K ...