springMVC4(7)模型视图方法源码综合分析

简介: <div class="markdown_views"><p>在完整web开发中,springMVC主要充当了控制层的角色。它接受视图层的请求,获取视图层请求数据,再对数据进行业务逻辑处理,然后封装成视图层需要的模型数据,再将数据导向到jsp等视图界面。 <br>在前面,我们通过对@RequestMapping和方法入参绑定的分析,完成了视图层->控制层的数据交接,然

在完整web开发中,springMVC主要充当了控制层的角色。它接受视图层的请求,获取视图层请求数据,再对数据进行业务逻辑处理,然后封装成视图层需要的模型数据,再将数据导向到jsp等视图界面。
在前面,我们通过对@RequestMapping和方法入参绑定的分析,完成了视图层->控制层的数据交接,然后业务逻辑处理主要由Service层进行。那么接下来很关键的就是,如何将视图数据导向到特定的视图中。

广泛意义上,视图,并非是单指前端界面如jsp\html等,我们可能需要给安卓、IOS等写后台接口、因前后端分离而放弃视图界面导向如对前端ajax请求的纯数据流输出等。这时,我们的视图可以为json视图、xml视图、乃至PDF视图、Excel视图等

springMVC为我们提供了多种途径输出模型数据:

输出途径 功能说明
ModelAndView 将处理方法返回类型设为ModelAndView,里面封装了我们的模型数据,同时指明了视图导向。
@modelAttribute 方法入参标注改注解后,入参的对象就会放到数据模型中。
Map及Model 入参为org.springframework.ui.Model或org.springframework.ui.ModelMap或java.util.Map时,方法返回时会将Map中的数据自动添加到模型中
@SessionAttributes 将模型中的某个属性暂存到HttpSession中,以便多个请求之间完成属性共享

下面我们主要介绍ModelAndView
ModelAndView(下面简称MAV)就像它的名字一样,既包含了模型数据又包含视图信息,我们返回一个MAV,springMVC就会将模型数据转发给相应的视图界面。
在学习ModelAndView的使用方法前,我们先用肢解的方法学习其两个重要组成部分

1. model

model是一个接口,我们可以简单地将model的实现类理解成一个Map,将模型数据以键值对的形式返回给视图层使用。在springMVC中,每个方法被前端请求触发调用前,都会创建一个隐含的模型对象,作为模型数据的存储容器。这是一个Request级别的模型数据,我们可以在前端页面如jsp中通过HttpServletRequest等相关API读取到这些模型数据。
在model中,定义有如下常用接口方法:

    /**
     * 添加键值属性对
     */
    Model addAttribute(String attributeName, Object attributeValue);

    /**
     * 以属性的类型为键添加属
     */
    Model addAttribute(Object attributeValue);

    /**
     * 以属性和集合的类型构造键名添加集合属性,如果有同类型会存在覆盖现象
     */
    Model addAllAttributes(Collection<?> attributeValues);

    /**
     * 将attributes中的内容复制到当前的model中
     * 如果当前model存在相同内容,会被覆盖
     */
    Model addAllAttributes(Map<String, ?> attributes);

    /**
     * 将attributes中的内容复制到当前的model中
     * 如果当前model存在相同内容,不会被覆盖
     */
    Model mergeAttributes(Map<String, ?> attributes);

    /**
     * 判断是否有相应的属性值
     */
    boolean containsAttribute(String attributeName);

    /**
     * 将当前的model转换成Map
     */
    Map<String, Object> asMap();

如果我们添加的属性没有指定键名,我们称之为匿名数据绑定,它们遵循如下规则:
1. 对于普通数据类型,我们直接以类型(第一字母小写)作为键值
2. 对于集合类型(Collection接口的实现者们,包括数组),生成的模型对象属性名为“简单类名(首字母小写)”+“List”,如List生成的模型对象属性名为“stringList”,List生成的模型对象属性名为“userModelList”。

在ModelAndView中,我们更多的是直接操作modelMap和Map来完成我们的模型参数准备即可。modelMap继承自java.util.LinkedHashMap。它在LinkedHashMap的基础上,新增了很多便利的构造方法如:

public ModelMap(String attributeName, Object attributeValue) {
    addAttribute(attributeName, attributeValue);
}
public ModelMap addAllAttributes(Map<String, ?> attributes) {
    if (attributes != null) {
        putAll(attributes);
    }
    return this;
}

使用这些构造方法能进一步简化我们的模型数据封装。

2. view

view也是一个接口,它表示一个响应给用户的视图如jsp文件,pdf文件,html文件。
它有两个接口方法:
1. String getContentType():返回视图的内容类型
2. void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception:根据给定模型和web资源定义视图的渲染形式。
view接口有众多的实现类,如下图所示:
这里写图片描述
在spring中,通过ViewResolver来解析对应View实例的行为, 它的定义相当简单:

public interface ViewResolver {
    //通过view name 解析View
    View resolveViewName(String viewName, Locale locale) throws Exception;
}

spring为我们提供ViewResolver实现类用来解析不同的view:
这里写图片描述
在我们最开始配置springMVC核心文件时,就用到了InternalResourceViewResolver,它是一个内部资源视图解析器。会把返回的视图名称都解析为 InternalResourceView 对象, InternalResourceView 会把 Controller 处理器方法返回的模型属性都存放到对应的 request 属性中,然后通过 RequestDispatcher 在服务器端把请求 forword 重定向到目标 URL。下面我们来看配置实例:

<bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/views/"></property><!-- 前缀,在springMVC控制层处理好的请求后,转发配置目录下的视图文件 -->
    <property name="suffix" value=".jsp"></property><!-- 文件后缀,表示转发到的视图文件后缀为.jsp -->
</bean>

解析完Model and(和) View后,再来看我们的ModelAndView:

public class ModelAndView {

    //视图成员
    private Object view;


    //模型成员
    private ModelMap model;

    //是否调用clear()方法清空视图和模型
    private boolean cleared = false;


    /**
     * 空构造方法
     */
    public ModelAndView() {
    }

    /**
     * 简便地使用视图名生成视图,具体解析由DispatcherServlet的视图解析器进行
     */
    public ModelAndView(String viewName) {
        this.view = viewName;
    }

    /**
     * 指定一个视图对象生成视图
     */
    public ModelAndView(View view) {
        this.view = view;
    }

    /**
     * 指定视图名同时绑定模型数据,这里模型数据以追加的形式添加在原来的视图数据中
     */
    public ModelAndView(String viewName, Map<String, ?> model) {
        this.view = viewName;
        if (model != null) {
            getModelMap().addAllAttributes(model);
        }
    }

    /**
     * 指定一个视图对象同时绑定模型数据,这里模型数据以追加的形式添加在原来的视图数据中
     */
    public ModelAndView(View view, Map<String, ?> model) {
        this.view = view;
        if (model != null) {
            getModelMap().addAllAttributes(model);
        }
    }

    /**
     * 简便配置:指定视图名,同时添加单个属性
     */
    public ModelAndView(String viewName, String modelName, Object modelObject) {
        this.view = viewName;
        addObject(modelName, modelObject);
    }

    /**
     * 简便配置:指定一个视图对象,同时添加单个属性
     */
    public ModelAndView(View view, String modelName, Object modelObject) {
        this.view = view;
        addObject(modelName, modelObject);
    }


    /**
    * 设置当前视图名
    */
    public void setViewName(String viewName) {
        this.view = viewName;
    }

    /**
     * 获取视图名,如果当前视图属性为view而非名字(String)则返回null
     */
    public String getViewName() {
        return (this.view instanceof String ? (String) this.view : null);
    }

    /**
     * 设置当前视图对象
     */
    public void setView(View view) {
        this.view = view;
    }

    /**
     * 获取视图,如果非view实例,则返回null
     */
    public View getView() {
        return (this.view instanceof View ? (View) this.view : null);
    }

    /**
     * 判断当前视图是否存在
     */
    public boolean hasView() {
        return (this.view != null);
    }


    /**
     * 获取模型Map,如果为空,则新建一个
     */
    public ModelMap getModelMap() {
        if (this.model == null) {
            this.model = new ModelMap();
        }
        return this.model;
    }

    /**
     * 同getModelMap
     */
    public Map<String, Object> getModel() {
        return getModelMap();
    }


    /**
     * 添加单个键值对属性
     */
    public ModelAndView addObject(String attributeName, Object attributeValue) {
        getModelMap().addAttribute(attributeName, attributeValue);
        return this;
    }

    /**
     * 以属性类型为键添加属性
     */
    public ModelAndView addObject(Object attributeValue) {
        getModelMap().addAttribute(attributeValue);
        return this;
    }

    /**
     * 将Map中的所有属性添加到成员属性ModelMap中
     */
    public ModelAndView addAllObjects(Map<String, ?> modelMap) {
        getModelMap().addAllAttributes(modelMap);
        return this;
    }


    /**
     * 清空视图模型,并设为清空状态
     */
    public void clear() {
        this.view = null;
        this.model = null;
        this.cleared = true;
    }

    /**
     * 判断是否为不含视图和模型
     */
    public boolean isEmpty() {
        return (this.view == null && CollectionUtils.isEmpty(this.model));
    }

}
目录
相关文章
|
2月前
|
JSON 前端开发 Java
SpringMVC的架构有什么优势?——视图与模型(二)
SpringMVC的架构有什么优势?——视图与模型(二)
|
4月前
|
前端开发 JavaScript Java
让你了解什么是spring MVC模型数据(附大量代码)
让你了解什么是spring MVC模型数据(附大量代码)
45 0
|
8月前
|
JavaScript 前端开发 Java
SpringMVC 映射请求数据获取案例--模型数据
SpringMVC 映射请求数据获取案例--模型数据
62 0
|
10月前
|
设计模式 前端开发 Java
Spring MVC 源码分析
Spring MVC 源码分析
170 0
|
设计模式 前端开发 Java
【小家Spring】Spring MVC执行流程 FrameworkServlet、DispatcherServlet源码分析(processRequest、doDispatch)(下)
【小家Spring】Spring MVC执行流程 FrameworkServlet、DispatcherServlet源码分析(processRequest、doDispatch)(下)
|
前端开发 Java Spring
【小家Spring】Spring MVC执行流程 FrameworkServlet、DispatcherServlet源码分析(processRequest、doDispatch)(中)
【小家Spring】Spring MVC执行流程 FrameworkServlet、DispatcherServlet源码分析(processRequest、doDispatch)(中)
|
设计模式 前端开发 Java
【小家Spring】Spring MVC执行流程 FrameworkServlet、DispatcherServlet源码分析(processRequest、doDispatch)(上)
【小家Spring】Spring MVC执行流程 FrameworkServlet、DispatcherServlet源码分析(processRequest、doDispatch)(上)
【小家Spring】Spring MVC执行流程 FrameworkServlet、DispatcherServlet源码分析(processRequest、doDispatch)(上)
|
XML 前端开发 数据格式
springmvc源码分析
springmvc源码分析
103 0
springmvc源码分析
|
前端开发 Java 应用服务中间件
Spring Boot与Spring MVC集成启动过程源码分析
背后spring-boot到底做了什么使我们的工作如此简单,它如何将spring、spring-mvc、tomcat整合到一起的呢?接下来我们以项目启动角度来分析整个初始化过程。
2825 0