我们在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()));
}
}