文档章节

Spring 源码分析(四) ——MVC(二)概述

水门-kay
 水门-kay
发布于 2016/03/23 22:29
字数 3872
阅读 1467
收藏 11

前    言

        Spring 是一种企业应用开发框架,在实际开发中起到了应用平台的作用,有点像企业应用中的“操作系统”,从而为企业应用资源的使用提供一致的环境。具体来说,Spring 提供的框架特性有 IoC 容器、AOP、事务处理、ORM 等等。在实际的软件产品开发中,如果有某些特性的使用比较普遍,那么就可以考虑将这些特性作为框架特性来实现,然后通过框架特性进行有效的封装,从而提高应用开发效率。 

        而 Spring MVC 是 Spring 的一种重要模块,作为开源的 Java EE 应用框架,很多 Web 应用是由 Spring 来支撑的。在 Web 应用中,MVC 模式的使用已经广为人知,如果 Spring 没有自己实现的 MVC 模式的支持,那么作为一个整体解决方法,它是不完整的。 而且对于 Web UI 的开发来说,这也是一种非常不错的选择。 下面我们就慢慢来介绍。

       

MVC 详解

        简    介

        MVC 是 Model(模型)、View(视图)以及 Controller(控制器)的缩写。它是一种经典的软件架构模式。最早由 Trygve Reenskaug 在 1978 年提出,是 施乐帕罗奥多研究中心 (Xerox PARC)在 20 世纪 80 年代为程序语言 Smalltalk 发明的一种软件架构。MVC 模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式通过对复杂度的简化,使程序结构更为直观。软件系统通过对自身基本部分分离的同时也赋予了各个基本部分应有的功能。

        MVC 模式在 UI 设计中使用的非常普遍,在著作《面向模式的软件体系结构》中,开篇就提到了这个模式。这个模式的主要特点是:分离了模型、视图、控制器三种角色,将业务处理从 UI 设计中独立出来,封装到模型和控制器设计中去,使得它们互相之间解耦,从而可以独立扩展而不需要互相依赖。


        架构内容

        在 Java 领域最经典的 MVC 实现方式是:JSP + Servlet + Javabean,虽然,现在已经被 HTML + AJAX + Servlet + JSON + POJO 这样的实现方式所取代。尽管已经有了许多的不同,但两者本质还是一样的,我们就以经典模式进行分析。

        在最初的 JSP 网页中,像数据库查询语句(SQL query)这样的数据层代码和像 HTML 这样的表示层代码是混在一起。虽然经验比较丰富的开发者会将数据从表示层中分离开来,但这样的良好设计通常并不是很容易做到的,实现它需要精心地计划以及不断地尝试。而 MVC 可以从根本上强制性地将它们分开,尽管构造 MVC 应用程序需要一些额外的工作,但是它带给我们的好处是毋庸置疑的。下面是 MVC 的基本原理图:

        前面已经提过,MVC 模式将一个交换式应用程序分为三个组件: Model、View 以及 Controller。MVC 模式在概念上强调 Model、View、Controller 的分离,各个模块也遵循着由 Controller 来处理消息,Model 控制数据源,View 负责数据显示地职责分离原则,因此在实现上,MVC 模式的 Framework 通常会将 MVC 三个部分分离实现:Model 负责数据访问,较现代的 Framework 都会建议使用独立的数据对象(DTO、POCO、POJO等等)来代替弱类型的集合对象。数据访问的代码会使用 Data Access 的代码或是 ORM-based Framework,也可以进一步使用 Repository Pattern 与 Unit of Works Pattern 来切割数据源的相依性;Controller 负责处理消息,较高级的 Framework 会有一个默认的实现来作为 Controller 的基础,例如 Spring 的 DispatcherServlet 或是 ASP.NET MVC 的 Controller 等,在职者分离原则上,每个 Controller 负责的部分不同,因此会将各个 Controller 切割成不同的文件以利维护;View 负责显示数据,这个部分多为前端应用,而 Controller 会有一个机制将处理的结果 (可能是 Model, 集合或是状态等) 交给 View,然后由 View 来决定怎么显示。例如 Spring Framework 使用 JSP 或相应技术,ASP.NET MVC 则使用 Razor 处理数据的显示。其中,Model 部分是它的核心功能,但在使用上还是颇为讲究的。

        首先,多个 View 能共享一个 Model 。同一个 Web 应用程序会提供多种用户界面,例如用户希望既能够通过浏览器来收发电子邮件,还希望通过手机来访问电子邮箱,这就要求 Web 网站同时能提供 Internet 界面或者 WAP 界面。在 MVC 设计模式中, Model 响应用户请求并返回响应数据,View 负责格式化数据并把它们呈现给用户,业务逻辑和表示层分离,同一个 Model 可以被不同的 View 重用,所以大大提高了代码的可重用性。

        其次,Controller 是自包含(self-contained)指高独立内聚的对象,与 Model 和 View 保持相对独立,所以可以方便的改变应用程序的数据层和业务规则。例如,把数据库从 MySQL 移植到 Oracle,或者把RDBMS 数据源改变成 LDAP 数据源,只需改变 Model 即可。一旦正确地实现了控制器,不管数据来自数据库还是 LDAP 服务器,View 都会正确地显示它们。由于 MVC 模式的三个模块相互独立,改变其中一个不会影响其他两个,所以依据这种设计思想能构造良好的少互扰性的构件。

        此外,Controller 提高了应用程序的灵活性和可配置性。Controller 可以用来连接不同的 Model 和 View 去完成用户的需求,也可以构造应用程序提供强有力的手段。给定一些可重用的 Model 、 View 和Controller 可以根据用户的需求选择适当的 Model 进行处理,然后选择适当的的 View 将处理结果显示给用户。


        适用范围

        因为 MVC 模式强调职责分离,所以在发展 MVC 应用时会产生出很多文件,在 IDE (集成开发环境) 不够成熟时它会是个问题,但在现代主流 IDE 都能使用类对象的信息来组织代码编辑的情况下,多文件早已不是问题,而且 MVC 模式会要求开发者进一步思考应用程序的架构 (Application Architecture),而非用大杂烩的方式开发应用程序,对于应用程序的生命周期以及后续的可扩充与可维护性而言有相当正面的帮助。另外,MVC 职责分离也带来了一个现代软件工程要求的重要特性:可测试性 (Testability),MVC-based 的应用程序在良好的职责分离的设计下,各个部分可独立行使单元测试,有利用与企业内的自动化测试、持续集成 (Continuous Integration) 与持续发行 (Continuous Delivery) 流程集成,减少应用程序改版部署所需的时间。

        MVC 模式的应用程序的目的就是希望打破以往应用程序使用的大杂烩程序撰写方式,并间接诱使开发人员以更高的架构导向思维来思考应用程序的设计,因此对于一个刚入门的初学者来说,架构导向的思考会有一定的门槛,需要较多的实现与练习才能具备相应的能力,大多数的初学者还是较习惯于大杂烩式的程序撰写,所以可能会对 MVC 模式抱持着排斥或厌恶的心态,然而 MVC (或是其他的Design Patterns) 都是有助于应用程序长远的发展,虽然大杂烩式的程序也可以用来发展长生命周期的应用程序,但是相较于 MVC,大杂烩式的程序在可扩充性和可维护性 (尤其是可测试性) 上会远比 MVC 复杂很多,相反的,MVC 模式的应用程序是在初始开发时期必须先思考并使用软件架构,使得开发时期会需要花较多心力,但是一旦应用程序完成后,可扩充性、可维护性和可测试性反而会因为 MVC 的特性而变得容易。

        因此,MVC 模式在已有众多优秀 Framework 的现代,早就己经没有不适合小型应用的问题,小型的应用还是可以由 MVC Framework 的应用来获取 MVC 的优点,同时它也能作为未来小型应用扩充到大型应用时的基础与入门砖。若一开始就想要做大型应用,那么 MVC 模式的职责分离以及要求开发的架构思考会更适合大型应用的开发。


Spring MVC 

        框架基础

        根据需求,Spring 提供了一个 Web 框架,它建立在 Spring 的核心概念 —— IoC 的应用上下文之上,并将其扩展到 Web 环境,与中间层应用上下文之间无缝结合。由于 Spring 的分层架构,Spring 中间层上下文也能轻易地集成到各种不同的 Web 框架中。当然,要达到这样的分层架构,中间层的接口设计就是至关重要的了。

        Spring 和专用 Web 框架(例如 Struts、WebWork 和 Tapestry)之间最根本的区别是:Spring 将它的 Web MVC 框架作为总体方案架构的一部分,因此它被设计为松散耦合,并且无缝地和 Spring 中间层丰富的 IoC 和 AOP 管理功能融为了一体,这也是 Spring MVC 的特点。


        设计目的

        最初的 J2EE Web 层实现通常是:在 JSP 页中包含过多的 Java 脚本(scriptlet),其中混乱地杂合了表达和业务逻辑。它导致的结果是一个不可测试的表现层、令人头痛的重构,以及维护的地狱。即使不是大多数,至少也有许多 J2EE Web 开发者在缺少经验时实现了这样的应用程序;而更多的人是接手维护和升级了这样的代码,并恼怒地咒骂最初的开发者。

        最原始的以 JSP 为中心的编程模型出自 ASP 和 PHP,这是 1996 年之后流行的两个 Web 编程环境。在这两个环境中,以页面为中心的脚本是仅有的选择,无法轻松实现分开流程控制和视图呈现,也无法轻松地将业务委派给逻辑组件。在 ASP.NET 和 PHP4 之后,它们都有了很大的改进,都为应用程序架构提供了更好的选择,可惜传统的编程模型仍然有着很大的影响力。

        所以,在一个分层体系的 J2EE 应用程序中,Web 层结构应该比这样的脚本页面更好,这样就可以像中间层组件一样有着可维护性、可测试性和可重用性。                


        实现基础

        Spring MVC 框架是一个基于请求驱动的 Web 框架,并且也使用了前端控制器模式来进行设计,再根据请求映射规则发送给相应的页面控制器(动作/处理器)进行处理。下面是 Spring MVC 处理请求的流程:

        具体执行步骤如下:

                1、首先用户发送请求——前端控制器,前端控制器根据请求信息(如 URL)来决定选择哪一个页面控制器进行处理并把请求委托给它,即以前的控制器的控制逻辑部分也就是1、2步骤;

                2、页面控制器接收到请求后,进行功能处理,首先需要收集和绑定请求参数到一个对象,这个对象在 Spring MVC 中叫 命令对象,并进行验证,然后命令对象委托给业务对象进行处理;处理完毕后返回一个 ModelAndView(模型数据和逻辑视图名),也就是3、4、5步骤;

                3、前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,并把模型数据传入以便视图渲染,步骤6、7;

                4、前端控制器再次收回控制权,将响应返回给用户,从8到整个结束。


        核心架构

        为完成以上的具体执行步骤,Spring MVC 将各个角色的职责进行了清晰的划分:前端控制器(DispatcherServlet)、请求到处理器映射(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)、处理器或页面控制器(Controller)、验证器(Validator)、命令对象(Command 命令对象)、表单对象(Form Object 表单对象)。

        它的核心开发步骤是:

                1、DispatcherServlet 在 web.xml 中的部署描述,从而拦截请求到 Spring MVC;

                2、HandlerMapping 的配置,从而将请求映射到处理器;

                3、HandlerAdapter 的配置,从而支持多种类型的处理器;

                4、ViewResolver 的配置,从而将逻辑视图名解析为具体视图技术;

                5、Controller 的配置,从而进行功能处理。


        下面是 Spring MVC 核心架构:

        核心架构的具体步骤如下:

                1、首先用户发送请求——DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;

                2、DispatcherServlet——HandlerMapping,HandlerMapping 将会把请求映射为 HandlerExecutionChain 对象(包含一个 Handler 处理器(页面控制器)对象、多个 HandlerInterceptor 拦截器)对象,通过这种策略模式,很容易添加新的映射策略;

                3、DispatcherServlet——HandlerAdapter,HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;

                4、HandlerAdapter——处理器功能处理方法的调用,HandlerAdapter 将会根据适配的结果调整真正的处理器的功能处理方法,完成功能处理;并返回一个 ModelAndView 对象(包含 模型数据、逻辑视图名);

                5、ModelAndView 的逻辑视图名——ViewResoler,ViewResoler 将吧逻辑视图名解析为具体的 View,通过这种策略模式,很容易更换其他视图技术;

                6、View——渲染,View 会根据传进来的 Model 模型数据进行渲染,此处的 Model 实际是一个 Map 数据结构,因此很容易支持其他视图技术;

                7、返回控制权给 DispatcherServlet,由 DispatcherServlet 返回响应给用户,到此一个流程结束。


        好了,至此架构方面就讲完了,再往下就是源码分析了。



——水门(2016年3月写于武汉)

© 著作权归作者所有

水门-kay
粉丝 459
博文 19
码字总数 59660
作品 0
杭州
后端工程师
私信 提问
Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密...

小致Daddy
2018/08/03
16.3K
1
早前学习Java记录

Spring 对 iBATIS 的支持】 Spring 通过 DAO 模式,提供了对 iBATIS 的良好支持。 SqlMapClient:是 iBATIS 中的主要接口,通过 xml 配置文件可以让 Spring 容器来管理 SqlMapClient 对象的创...

大风厂蔡成功
2016/07/10
43
0
springmvc源码解析合集

更多精彩源码解析文章请关注”天河聊架构“微信公众号。 springmvc源码解析之组件介绍 springmvc源码解析之配置加载SpringServletContainerInitializer springmvc源码解析之配置加载Context...

天河2018
03/27
160
0
网关 Spring-Cloud-Gateway 源码解析 —— 网关初始化

网关 Spring-Cloud-Gateway 源码解析 —— 网关初始化 Harries Blog™2017-12-135 阅读 SpringAppclasspathcatbeanAPIbuildbug 本文主要基于 Spring-Cloud-Gateway 2.0.X M4 摘要: 原创出处 ......

Harries Blog™
2017/12/13
0
0
Java系列文章(全)

JVM JVM系列:类装载器的体系结构 JVM系列:Class文件检验器 JVM系列:安全管理器 JVM系列:策略文件 Java垃圾回收机制 深入剖析Classloader(一)--类的主动使用与被动使用 深入剖析Classloader(二...

www19
2017/07/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如何设计抗住100亿次请求的抢红包系统?(附GitHub代码)

1. 前言 前几天,偶然看到了 《扛住100亿次请求——如何做一个“有把握”的春晚红包系统”》一文,看完以后,感慨良多,收益很多。 正所谓他山之石,可以攻玉,虽然此文发表于2015年,我看到...

Java程序员之家
42分钟前
4
0
动图+源码,演示Java中常用数据结构执行过程及原理

最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程. 主要基于jdk8, 可能会有些特性与jdk7之前不相同, 例如LinkedList LinkedHashMap中的双向...

Java技术剑
今天
5
0
怎样在ps中制作对话气泡?一招教你轻松解决

PS是在工作中经常使用的平面设计软件,利用ps可以实现很多操作。换天,换发色,添加亮灯等操作都是比较常见的,今天将为大家分享怎样在ps中制作对话气泡的方法,希望能给大家带来帮助。 绘制...

干货趣分享
今天
2
0
EDI 电子数据交换全解指南

EDI(Electronic Data Interchange,电子数据交换)技术使得企业与企业(B2B)实现通信自动化,帮助交易伙伴和组织更快更好地完成更多工作,并消除了人工操作带来的错误。从零售商到制造商、物...

EDI知行软件
今天
3
0
CentOS7的LVM动态扩容

# 问题 CentOS7上面的磁盘空间有点紧张,需要扩容。 解决 查询当前磁盘状态 [root@xxx ~]# lsblkNAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTfd0 2:0 1 4K ...

亚林瓜子
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部