Spring MVC源码分析之ModelFactory

我们在RequestMappingHandlerAdapter的invokeHandlerMethod方法中可以看到对ModelFactory的使用

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
	ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
	...
	modelFactory.initModel(webRequest, mavContainer, invocableMethod);
	...
	return getModelAndView(mavContainer, modelFactory, webRequest);
}

ModelFactory主要包括两个功能,初始化Model和将Model中的参数更新到SessionAttributes中。

initModel

public final class ModelFactory {
    public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
            throws Exception {
    // 获取使用@SessionAttributes注解并已经解析的参数,合并到mavContainer
        Map<String, ?> attributesInSession = this.sessionAttributesHandler.retrieveAttributes(request);
        mavContainer.mergeAttributes(attributesInSession);
    // 执行使用@ModelAttribute注解的方法,并将结果设置到mavContainer
        invokeModelAttributeMethods(request, mavContainer);
    // 将同时使用@ModelAttribute和@SessionAttributes注解的参数设置到mavContainer
        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);
            }
        }
    }
    // ...
}

ModelAndViewContainer

先看看源码

public class ModelAndViewContainer {
 
     // 若为true,处理器返回redirect属兔是一定不使用defaultModel
	private boolean ignoreDefaultModelOnRedirect = false;
	
	//视图,可以是实际视图也可以是String类型的逻辑视图
	private Object view;
 
	//默认使用的Model,ModelMap可以理解就是一个hashmap结构
	private final ModelMap defaultModel = new BindingAwareModelMap();
	
	//redirect类型的Model
	private ModelMap redirectModel;
 
	private boolean redirectModelScenario = false;
	
	//用于设置SessionAttribute使用完的标志
	private final SessionStatus sessionStatus = new SimpleSessionStatus();
 
	//请求是否已经处理完成的标志
	private boolean requestHandled = false;
 
 
	
	public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) {
		this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect;
	}
 
	//设置视图名称
	public void setViewName(String viewName) {
		this.view = viewName;
	}
 
	
	public String getViewName() {
		return (this.view instanceof String ? (String) this.view : null);
	}
 
	
	public void setView(Object view) {
		this.view = view;
	}
 
	public Object getView() {
		return this.view;
	}
 
	public boolean isViewReference() {
		return (this.view instanceof String);
	}
 
 
	public ModelMap getModel() {
		if (useDefaultModel()) {
			return this.defaultModel;
		}
		else {
			return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
		}
	}
 
	//和model相关的处理方法
     /*
       * 假设redirectModelScenario = R ,ignoreDefaultModelOnRedirect = I ,(redirectModel == null)= M
       * 那么(R, I, M)共有8中组合情况,useDefaultModel返回false(也就是使用redirectModel)只有三种情况:
       * (1,1,0)、(1,1,1)、(1,0,0)
       * a:如果同时设置了redirectModelScenario和ignoreDefaultModelOnRedirect为true,那么无论redirectModel
       *    是否为null,都会使用redirectModel;
       * b:如果设置了redirectModelScenario为true,而ignoreDefaultModelOnRedirect为false,同时redirectModel
       *    为null,那么也会使用redirectModel;
	*/
	private boolean useDefaultModel() {
		return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
	}
 
	public ModelMap getDefaultModel() {
		return this.defaultModel;
	}
 
	public void setRedirectModel(ModelMap redirectModel) {
		this.redirectModel = redirectModel;
	}
 
	public void setRedirectModelScenario(boolean redirectModelScenario) {
		this.redirectModelScenario = redirectModelScenario;
	}
 
	public SessionStatus getSessionStatus() {
		return this.sessionStatus;
	}
 
	
	public void setRequestHandled(boolean requestHandled) {
		this.requestHandled = requestHandled;
	}
 
	
	public boolean isRequestHandled() {
		return this.requestHandled;
	}
 
	public ModelAndViewContainer addAttribute(String name, Object value) {
		getModel().addAttribute(name, value);
		return this;
	}
 
 
	public ModelAndViewContainer addAttribute(Object value) {
		getModel().addAttribute(value);
		return this;
	}
 
	public ModelAndViewContainer addAllAttributes(Map<String, ?> attributes) {
		getModel().addAllAttributes(attributes);
		return this;
	}
 
	public ModelAndViewContainer mergeAttributes(Map<String, ?> attributes) {
		getModel().mergeAttributes(attributes);
		return this;
	}
 
	public ModelAndViewContainer removeAttributes(Map<String, ?> attributes) {
		if (attributes != null) {
			for (String key : attributes.keySet()) {
				getModel().remove(key);
			}
		}
		return this;
	}
 
	
	public boolean containsAttribute(String name) {
		return getModel().containsAttribute(name);
	}
 
}

ModelAndViewContainer主要是用来管理Model对象和view对象,在ModelAndViewContainer中有defaultModel和redirectModel,
defaultModel是默认使用的Model,后者用于传递redirect时的参数,我们在处理中使用了Model或ModelMap时,ArgumentResolver会传入defaultModel,它是BindingAwareModelMap类型,既继承了ModelMap又实现了Model接口,所以在处理器中使用Model或者ModelMap其实使用的是同一个对象,Map参数传入的也是这个对象。处理器中RedirectAttributes类型的参数ArgumentResolver会传入redirectModel,它实际上是RedirectAttributeModelMap类型。

SessionAttributesHandler

SessionAttributes

SessionAttributesHandler其实就是解析被@SessionAttribute注解的处理器
我们先看一个使用SessionAttributes的例子

@Controller
@RequestMapping("/user")
@SessionAttributes(value ={"name"})
public class UserController {
	
	@RequestMapping("/index")
	public String index(Model model){
		model.addAttribute("name", "小明");
		model.addAttribute("price", new Double("1000.00"));
		//跳转之前将数据保存到name中,因为注解@SessionAttribute中有这几个参数
		return "redirect:get.action";
	}
}

@SessionAttribute作用于处理器类上,用于在多个请求之间传递参数,类似于Session的Attribute,但不完全一样,一般来说@SessionAttribute设置的参数只用于暂时的传递,而不是长期的保存,长期保存的数据还是要放到Session中。
通过@SessionAttribute注解设置的参数有3类用法:

(1)在视图中通过request.getAttribute或session.getAttribute获取

(2)在后面请求返回的视图中通过session.getAttribute或者从model中获取

(3)自动将参数设置到后面请求所对应处理器的Model类型参数或者有@ModelAttribute注释的参数里面。

将一个参数设置到SessionAttribute中需要满足两个条件:

(1)在@SessionAttribute注解中设置了参数的名字或者类型

(2)在处理器中将参数设置到了model中。
用户使用后可以调用SessionStatus.setComplete来清除,这个方法只是清除SessionAttribute里的参数,而不会应用Session中的参数。

updateModel

package org.springframework.web.method.annotation;
public final class ModelFactory {
    // ...
    public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
        if (mavContainer.getSessionStatus().isComplete()){ // 清除
            this.sessionAttributesHandler.cleanupAttributes(request);
        }
        else { // 不清除,那么就需要同步
            this.sessionAttributesHandler.storeAttributes(request, mavContainer.getModel());
        }

        if (!mavContainer.isRequestHandled()) {
            updateBindingResult(request, mavContainer.getModel());
        }
    }

    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);
            // 核对是否需要绑定BindingResult到model
            if (isBindingCandidate(name, value)) {
                String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;

                if (!model.containsAttribute(bindingResultKey)) { // 不是其他参数绑定的结果
                    WebDataBinder dataBinder = binderFactory.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;
        }
        // 是否在@SessionAttributes注解定义中
        Class<?> attrType = (value != null) ? value.getClass() : null;
        if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, attrType)) {
            return true;
        }
        // 不是null,数组,集合,map,简单数据类型,则调用
        return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
                !(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值