Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

MVC模式

20
Mar
2008

MVC模式

By Alex
/ in Architecture
/ tags MVC, 架构模式
0 Comments
模式说明
经典的MVC模式

MVC,即模型-视图-控制器,是一种用于用户界面设计/展示层的架构模式,起初出现在Smalltalk。MVC模式的核心是利用控制器作为中介,将视图和模型解耦。

MVC模式中三者的职责分工明确:

  1. 模型(Model):持有所有的数据、状态和程序逻辑,模型不会感知到视图和控制器(下层组件不依赖于上层)。模型提供了操作和检索状态的接口,并且将状态改变信息推送给观察者(视图),在某些设计中,视图需要的数据被一同推送
  2. 视图(View):用来呈现模型,视图通常直接从模型中获取它需要显示的状态和数据
  3. 控制器(Controller):负责获取用户输入,并解读其对于模型的意义。控制器也可能直接要求视图做出改变。控制器不应当包含业务逻辑

注意和传统的模型含义不同,MVC中的模型不仅仅是指数据对象。以播放器为例,播放状态、当前歌曲、播放行为等都属于模型的组成部分。

三者的调用关系图如下:

patterns_MVC

上图虽然有箭头从Model指向View,但是这是一种松耦合的、基于观察者模式的通知方式。此外,某些设计中把Controller也作为Model的观察者,图中没有标出。

一般MVC中会用到三种设计模式:

  1. 观察者模式:Model作为事件源,View、Controller作为监听器
  2. 策略模式:对于View来说,Controller是具体策略,如果需要改变行为,可以更换一个Controller
  3. 组合模式:View中的GUI控件一般都呈现出树状组合关系
MVC2

经典的MVC模式适用于单机桌面程序的开发,由于Model、View运行在统一进程中,因而互相之间基于函数调用的通信很方便。在常见的Web应用场景中,Model是无法通知到View的,因为:

  1. 在使用服务器端脚本(PHP、JSP、ASP等)作为视图组件的情况下,这些视图都是一次性生成的过程,结果是HTML代码,并传递到浏览器端运行。这些视图无法后续接收通知(调用)
  2. 在使用RIA时,View完全运行在浏览器中,View与服务器端只有数据的交互,同样不能接收Model的通知(调用)

为了解决这一问题,MVC2架构出现了,MVC2将View和Model完全解耦,两者之间的交互完全通过Controller间接完成。MVC2中三者的调用关系图如下:

patterns_MVC2

随着基于JavaScript的UI框架以及WebSocket等协议的兴起,现在很多应用抛弃了服务器端脚本技术。View和Controller之间的通信完全基于数据而不是函数调用,WebSocket支持双向通信,这意味着Model在数据发生变化时即时通知Controller,由后者推送给View成为可能。这样的应用场景导致MVC架构又有了新的变化:

patterns_MVC2_2

经典应用
Servlet与JSP

JavaEE中的Servlet与JSP技术可以用来构建MVC模式,其中Servlet充当Controller、JSP充当View、后端的Bean则充当Model:

patterns_MVC_Servlet

Struts 1.x

Struts 是早期的Java Web框架,其内置了MVC模式的支持。Struts的核心接口是Action,它是一个HTTP请求与相应业务逻辑之间的适配器,作为Controller的RequestProcessor负责把请求分发给合适的Action;Struts没有对View和Model建模,但是在视图方面提供了多种标签库,简化开发。在Struts,ActionForm被用来作为数据传输对象,可以在Model和View之间传递信息。Struts的核心类图如下:

patterns_MVC_struts

Struts 2.x

Struts2源于Webwork框架,它朝向零配置的方向发展,原先需要通过XML进行的配置,现在都可以使用Annotation完成。在Struts2中Controller的角色由FilterDispatcher承担,与Struts1中的ActionServlet不同,该类是一个Servlet过滤器;Model角色部分由Action类承担;View角色则部分由Result接口承担。

Struts2最大的不同是,Action没有接口约束,它的角色(该角色实质是由它的一个方法承担)也变成Model,框架通过反射调用之。Action类上可以通过Annotation附加大量元数据信息,例如Action负责哪些请求的处理,请求处理完毕后转向什么页面,下面是一个例子:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.ExceptionMapping;
import org.apache.struts2.convention.annotation.ExceptionMappings;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.Results;
 
import com.opensymphony.xwork2.ActionSupport;
 
@ParentPackage ( "struts-default" )
@Namespace ( "/web-manager" )
@Results ( { @Result ( name = "success", location = "/main.jsp" ),
        @Result ( name = "error", location = "/error.jsp" ) } )
@ExceptionMappings ( { @ExceptionMapping ( exception = "java.lang.RuntimeException", result = "error" ) } )
public class LoginAction extends ActionSupport
{
    private static final long serialVersionUID = 1L;
 
    @Action ( "login" )
    public String login() throws Exception
    {
        return SUCCESS;
    }
 
    @Action ( value = "add", results = { @Result ( name = "success", location = "/index.jsp" ) } )
    public String add() throws Exception
    {
        return SUCCESS;
    }
}

Struts2的类图如下(使用带标签的序号说明了某个场景下的调用顺序):

patterns_MVC_Struts2

可以看到,Struts2处理HTTP请求的大致流程如下:

  1. FilterDispatcher拦截到来自Servlet容器的HTTP请求,创建ActionContext,该对象是Action执行的上下文,并依据HttpRequest和ConfigurationManager获取ActionMapping,然后调用Dispatcher.serviceAction(),进行分发处理
  2. ActionMapping知道哪个类的方法负责处理当前请求,Dispatcher根据ActionMapping创建extraContext,这是一个简单的Map,然后根据extraContext创建ActionProxy,后者是代理模式的实现。Struts2使用的代理类是StrutsActionProxy。Dispatcher调用ActionProxy.execute(),将请求的处理权转给StrutsActionProxy
  3. StrutsActionProxy没有任何状态,因而不需要为每次请求重新创建它,该代理会在请求处理前后设置/恢复线程本地的ActionContext信息,这样在整个处理期间,ActionContext都不需要通过方法参数传递了。代理通过调用ActionInvocation.invoke(),转移请求处理器
  4. ActionInvocation是Structs2请求处理的核心,主要干三件事情:
    1. 调用Interceptor链条,对请求进行处理,这里是职责链模式的应用
    2. 通过反射调用Action的方法,Action是用户程序的主要扩展点
    3. 调用Result,处理视图
Spring MVC

Spring的MVC框架和Struct2的思想很相似,都是倾向于走向零配置、大量使用注解。Spring MVC中的Controller类与Struct2的Action在功能上是相当的,但是Spring MVC倾向于将其划分到C而不是M;DispatcherServlet是Spring MVC的(核心)控制器角色;ModelAndView类对V、M进行了通用建模,不过很明显了,Spring MVC把Model的概念弱化了,在它这里,M仅仅是DTO。

SpringMVC的处理流程比Struts2清晰简明,逻辑一直把握在DispatcherServlet手上:

  1. DispatcherServlet使用模板方式模式的变体来组织整个请求处理过程,它定义了整体处理算法:请求预处理⇨设置属性⇨调用拦截器预处理⇨调用Handler⇨调用拦截器后处理⇨处理渲染⇨调用拦截器完成处理。这些具体步骤中,很大一部分交由用户提供的拦截器、Handler完成。Handler可以是任意Java类,只需要将其标记@Controller注解并扫描。
  2. DispatcherServlet能够接受到Spring事件通知,监听器是它父类的一个内部类ContextRefreshListener,该内部类转调了DispatcherServlet的onApplicationEvent
  3. DispatcherServlet设置当前HttpServletRequest对象的若干属性,例如它关联的ApplicationContext
  4. DispatcherServlet执行拦截器链条,这是一个职责链模式的变体,具有3条职责路径(预处理、后处理、完成后处理),首先执行preHandle()进行预处理
  5. DispatcherServlet调用HandlerAdapter.handle()处理请求,最常用的HandlerAdapter实现是AnnotationMethodHandlerAdapter,基于注解使用Spring MVC时,会自动使用该类。从名字上可以看出,这是一个适配器模式,它基于反射机制,对Handler继续完全的适配:
    1. Handler的入参数量、类型都是任意的,HandlerAdapter需要把HttpServletRequest、HttpServletResponse等对象适配为这些类型。AnnotationMethodHandlerAdapter依靠反射机制和各种注解完成这一适配
    2. Handler的返回值类型是任意的,HandlerAdapter需要将其适配为ModelAndView类型
  6. DispatcherServlet执行拦截器链条的postHandle()进行后处理
  7. DispatcherServlet调用View.render()执行渲染
  8. DispatcherServlet执行拦截器链条的afterCompletion()进行最终处理

Spring MVC的主要类图如下:

patterns_MVC_Spring

下面是一个简单的Handler的例子,可以看到和Struts的Action是很类似的:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
 
@Controller
@RequestMapping ( "/web-manager" )
public class LoginController
{
 
    @RequestMapping ( value = "login/{mode}", method = { RequestMethod.POST } )
    @ResponseBody
    public Object login( @PathVariable String mode, @RequestParam String uname )
    {
        return null;
    }
}
ExtJS的MVC框架

ExtJS是一个基于JavaScript的框架,它提供了大量基于Web的UI控件,ExtJS还内置了MVC模式,具体可以参考:ExtJS 4的MVC框架

在这里我们可以注意到,理解MVC的概念不能死板。假设我们同时使用Spring MVC和ExtJS MVC,那么这两个框架什么关系呢?岂不是重复了?实际上,对于ExtJS来说,整个服务器端应用程序都属于M的一部分;而对于Spring来说,整个ExtJS和Spring的JsonView都属于V的部分。角度不同,思考也就不同,所谓横看成岭侧成峰嘛。

← log4j配置文件样本
CSS知识集锦 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • 反应器模式
  • ExtJS 4的MVC框架
  • SpringMVC知识集锦
  • 享元模式
  • 面向对象的设计原则

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • Bazel学习笔记 38 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2