mvc 订阅
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。 展开全文
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
信息
外文名
MVC框架
全    名
Model View Controller
类    别
软件构件模式
中文名
MVC
架构内容
视图,模型,控制器
MVC框架简介
MVC开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。比如一批统计数据可以分别用柱状图、饼图来表示。C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。 [1-2]  模型-视图-控制器(MVC)是Xerox PARC在二十世纪八十年代为编程语言Smalltalk-80发明的一种软件设计模式,已被广泛使用。后来被推荐为Oracle旗下Sun公司Java EE平台的设计模式,并且受到越来越多的使用ColdFusion和PHP的开发者的欢迎。模型-视图-控制器模式是一个有用的工具箱,它有很多好处,但也有一些缺点。 [3]  (概述内容来源: [4]  )
收起全文
精华内容
参与话题
问答
  • ASP.NET MVC5 提升篇

    千人学习 2018-10-06 21:43:15
    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化...
  • MVC简述

    千次阅读 多人点赞 2015-01-31 17:08:46
    我们已经学习了三层架构,现在学习MVC架构,很多人都会把三层和MVC混淆,那么我们就来看看MVC的庐山真面目。  一、定义  MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的...

          我们已经学习了三层架构,现在学习MVC架构,很多人都会把三层和MVC混淆,那么我们就来看看MVC的庐山真面目。


          一、定义

           MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。

          在MVC设计模式中:

          Model是指要处理的业务逻辑和数据操作,它接收视图请求的数据并返回最终的处理结果;

          View视图主要是指的跟用户打交道并且显示给用户看的;

          Controller看成是Model和View的桥梁,枢纽,响应请求,处理跳转,使模型和视图保持一致。


         二、处理流程图

          

          对于每一个用户输入的请求,首先被控制器接收,并决定由哪个模型来进行处理,然后模型通过业务处理逻辑处理用户的请求并返回数据,最后控制器用相应的视图格式化模型返回的数据,并通过显示页面呈现给用户。


          三、优点
           1、视图重用。多个视图能共享一个模型。同一个模型可以被不同的视图重用,大大提高了代码的可重用性。


           2、松散耦合。由于MVC的三个模块相互独立,改变其中一个不会影响其他两个,所以依据这种设计思想能构造良好的松耦合的构件。


           3、灵活性。控制器提高了应用程序的灵活性和可配置性。控制器可以用来联接不同的模型和视图去完成用户的需求,这样控制器可以为构造应用程序提供强有力的手段。


        四、缺点
          1、增加了系统结构和实现的复杂性。
           对于简单的界面,严格遵循MVC,使模型、视图与控制器分离,会增加结构的复杂性,并可能产生过多的更新操作,降低运行效率。


          2、视图与控制器间的过于紧密的连接。
          视图与控制器是相互分离,但确实联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。


          3、视图对模型数据的低效率访问。
          依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。


         MVC的使用会使我们的软件或系统在健壮性,代码重用和结构方面有很大的提升。但并不是所有的系统都适合使用MVC,它会带来额外的工作和复杂性,所以并不适合小型甚至中等规模的系统或软件,花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。这就需要我们做出抉择。

    展开全文
  • 什么是MVC

    千次阅读 2019-05-07 11:04:30
    什么是MVCMVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范。 用一种业务逻辑、数据、界面显示分离的方法,将业务逻辑聚集到一个部件里面,在改进和个性...

    什么是MVC?

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范。

    用一种业务逻辑、数据、界面显示分离的方法,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。

    Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
      通常模型对象负责在数据库中存取数据。
    View(视图)是应用程序中处理数据显示的部分。
      通常视图是依据模型数据创建的。
    Controller(控制器)是应用程序中处理用户交互的部分。
      通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。

    MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们各自处理自己的任务。最典型的MVC就是JSP + servlet + javabean的模式
    Model:常用javabean去实现,通过各种类来对数据库的数据进行获取,并封装在对象当中。

    View:常用JSP来实现,通过可直接观察的JSP页面来展示我们从数据库中获取的数据。

    Controller:常用servlet来实现,通过servlet来获取经过javabean包装过的对象(已存入数据库中的数据),然后再发送数据传输到JSP界面。

    在这里插入图片描述
    在这里插入图片描述

    拓展

    什么是JavaBean?

    JavaBean是Java的可重用组件(Java类)。
    约束如下:

    1. 必须为public
    2. 必须有无参构造函数
    3. 必须通过公共方法暴露成员属性(get、set)
    展开全文
  • 深入理解MVC

    万次阅读 多人点赞 2018-04-30 17:56:05
    MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性、可移植性,代码的可重用性。...



    MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性、可移植性,代码的可重用性。


    MVC即Model、View、Controller即模型、视图、控制器。我在和同行讨论技术,阅读别人的代码时发现,很多程序员倾向于将软件的业务逻辑放在Controller里,将数据库访问操作的代码放在Model里。


    最终软件(网站)的代码结构是,View层是界面,Controller层是业务逻辑,Model层是数据库访问。


    不知道大家知不知道另外一种软件开发模式三层架构,它和MVC相似之处是也分为三层,分别是UI层表示用户界面,BLL层表示业务逻辑,DAL层表示数据访问。三层架构曾经红极一时,MVC大行其道之后它就销声匿迹了, 可现在看来, 它似乎只是改头换面, 装扮成MVC的样子,并且深受程序员们的欢迎,因为它的这种分层方式和前文描述的MVC如出一辙。


    再说的直白点,很多程序员将MVC当成了三层架构在用,这看起来似乎没什么问题,毕竟三层架构也是一种和MVC齐名的架构模式。可问题在于用三成架构的思路写MVC,那么写出来的东西既不是三成架构也不是MVC,到是像一个什么都不是四不像。熟悉天龙八部的同学应该知道这样一段情节,吐蕃番僧鸠摩智强行用道家的小无相功为基础修炼少林的七十二绝技和易筋经最终导致走火入魔。其实用这个例子来形容现在一些程序员用三层架构的思想写MVC最恰当不过了,三层架构的核心思想是面向接口编程和各层之间的解耦和可替换性,MVC框架中没有这种概念,因为MVC要面对的问题本就不是三成架构要面对的问题,所以以MVC为基础写出来的三成架构是不会具备三层架构的核心要义的,换言之,这种代码是放弃了三层架构和MVC的精华,获得了它们的糟粕,是愚蠢的编码方式。


    我吐槽了这么多,对于吐槽的理由要是说不出个所以然来,估计要被人喷死,下面就来说说MVC本质原理和正确使用方式,当然,这里的MVC指的最纯粹MVC,适合各类软件,而不仅仅指Web框架中的变体MVC,然而万变不离其宗,文中所述的MVC思想同样适用于Web开发。


    MVC要实现的目标是将软件用户界面和业务逻辑分离以使代码可扩展性、可复用性、可维护性、灵活性加强。


    View层是界面,Model层是业务逻辑,Controller层用来调度View层和Model层,将用户界面和业务逻辑合理的组织在一起,起粘合剂的效果。所以Controller中的内容能少则少,这样才能提供最大的灵活性。


    比方说,有一个View会提交数据给Model进行处理以实现具体的行为,View通常不会直接提交数据给Model,它会先把数据提交给Controller,然后Controller再将数据转发给Model。假如此时程序业务逻辑的处理方式有变化,那么只需要在Controller中将原来的Model换成新实现的Model就可以了,控制器的作用就是这么简单, 用来将不同的View和不同的Model组织在一起,顺便替双方传递消息,仅此而已。


    合理的使用MVC有很多好处,要一一道尽是一件异常困难的任务。在这里我们通过一个反面示例来侧面的证明正确使用MVC的好处与依据。


    如前文所言, 很多程序员偏爱于将业务逻辑放在Controller中,我极力反对这种做法,现在我就来证明这中做法的错误性。


    我们知道在写程序时,业务逻辑的重复使用是经常要面对的场景。 如果业务逻辑写在控制器中, 要重用它的唯一方法就是将它提升到父类之中,通过继承来达到代码复用的效果。但这么做会带来一个巨大的副作用,违背了一项重要的面向对象设计原则:接口隔离。


    什么是接口隔离,我在这里简单的讲述一下。通俗一点讲,接口隔离就是当一个类需要继承另一个类时, 如果被继承的类中有继承的类用不到的方法或者属性时,就不要去实现这个继承。如果真的情非得已必须要继承,那么也需要从被继承的类中再提取出一个只包含需要部分功能的新类型,最终去继承这个新类型才是正确的做法。 换句话说,实现继承的时候,不要去继承那些用不到的事物。


    回到之前的话题,通过继承父控制器的方式复用业务逻辑时,往往会出现为了重用一个方法而继承来一大堆用不到的方法,表面上看起来似乎没什么问题,但是这会使代码变的难以理解,
    长此以往, 软件的代码会朝着不健康的方向发展。


    要知道,使用继承的条件是很苛刻的,我们学习面向对象变编程继承特性时,第一课就是只有满足IS-A(是一个)关系时才可以使用继承,如果仅仅是复用代码,并不是我们使用继承的理由。使用组合是复用代码提倡的方式,也就是所谓的HAS-A(有一个)的关系,相信每个程序员都听过“少用继承,多有组合”这句话,这句话是软件开发业的先驱们千锤百炼总结出来的,值得我们去遵循。


    各Model之间是可以相互调用的, Controller也可以无障碍的调用Model,因此将业务逻辑放在Model中可以灵活的使用组合的方式复用代码。


    而Controller之间是不可以相互调用的,要复用代码只能将代码提升至父类,通过继承实现,显然这种做法既不正确,也不灵活,因此完全不提倡。


    综上所述,仅仅只是代码复用这一点,也足以将“厚Controller,薄Model”这种不健康的MVC思想打入十八层地狱。


    现在我们大概知道了代码应该如何分布于MVC三层之间, 知其然,并且也知其所以然。接下来我们再从另一个角度深刻剖析MVC,脱它个精光,让它赤条条展示在我们眼前。


    众所周知,GoF总结过23个设计模式,这23个设计模式是某些特定的编程问题的特效药,这是业内公认的。


    MVC是一种模式,但却在GoF总结出来的这个23个设计模式之外,确切的说它不是一种设计模式,它是多种设计模式的组合,并不仅仅只是一个单独的一个模式。


    组成MVC的三个模式分别是组合模式、策咯模式、观察者模式,MVC在软件开发中发挥的威力,最终离不开这三个模式的默契配合。 那些崇尚设计模式无用论的程序员,请了解只要你们使用MVC,就离不开设计模式。


    注意,以下内容以这三个设计模式的知识为基础,如果对这三个设计模式没概念,或许会阅读困难。


    先说组合模式在MVC中扮演什么样的角色。


    组合模式只在视图层活动, 视图层的实现用的就是组合模式,当然,这里指的实现是底层的实现,是由编程框架厂商做的事情,用不着普通程序员插手。


    组合模式的类层次结构是树状的, 而我们做Web时视图层是html页面,html的结构不正是树状的吗,这其实就是一个组合模式的应用,只是浏览器厂商已经把界面相关的工作帮我们做掉了,但它确确实实是我们应用MVC的其中一部分,只是我们感觉不到罢了,这也是我们觉得View是实现起来最简单最没有歧义的一层的原因。


    除网页以外的其他用户界面程序,如WPF、Android、ASP.NET等等都是使用树状结构来组织界面控件对象的,因为组合模式就是从界面设计的通用解决方案总提炼出来的。所以与其说MVC选择了组合模式,还不如说组合模式是必定会存在MVC中的,因为只要涉及到用户界面,组合模式就必定存。事实上即使不理解组合模式,也不影响程序员正确的使用MVC,组合模式本就存在于程序员接触不到的位置。


    然而,观察者模式和策略模式就显得比较重要,是实实在在MVC中接触的到的部分。


    观察者模式有两部分组成,被观察的对象和观察者,观察者也被称为监听者。对应到MVC中,Model是被观察的对象,View是观察者,Model层一旦发生变化,View层即被通知更新。View层和Model层互相之间是持有引用的。 我们在开发Web MVC程序时,因为视图层的html和Model层的业务逻辑之间隔了一个http,所以不能显示的进行关联,但是他们观察者和收听者的关系却没有改变。当View通过http提交数据给服务器,服务器上的Model接受到数据执行某些操作,再通过http响应将结果回送给View,View(浏览器)接受到数据更新界面,这不正是一个接受到通知并执行更新的行为吗,是观察者模式的另一种表现形式。


    但是,脱离Web,当通过代码去纯粹的表示一个MVC结构的时候,View和Model间无疑是观察者和被观察的关系,是以观察者模式为理论基础的。即使在Web中因为http壁垒的原因导致真正的实现有点走样,但是原理核心和思路哲学却是不变的。


    最后是策略模式。策略模式是View和Controller之间的关系,Controller是View的一个策略,Controller对于View是可替换的, View和Controller的关系是一对多,在实际的开发场景中,也经常会碰到一个View被多个Controller引用,这即使策咯模式的一种体现,只是不那么直观而已。


    总结一下,关于MVC各层之间关系所对应的设计模式


    View层,单独实现了组合模式

    Model层和View层,实现了观察者模式

    View层和Controller层,实现了策咯模式


    MVC就是将这三个设计模式在一起用了,将这三个设计模式弄明白,MVC将毫无神秘感可言。如果不了解这三个设计模式去学习MVC,那不管怎么学总归是一知半解,用的时候也难免不会出想问题。


    再次回到最前面讨论的业务逻辑应该放在Controller还是Model的问题上,从设计模式的角度讲,策略模式中的策略通常都很小很薄,不会包含太多的内容, Controller是一个策略, 自然不应该在里面放置过多的内容,否则要替换一个新的会相当麻烦,与此同时也会破坏View-Model的观察者模式,仿佛View-Controller之间即实现了策略模式又实现了观察者模式,这种混乱是罪恶的根源,是制造焦油坑让程序员陷入其中无法自拔的罪魁祸首。切忌,应当避免。



    作者:陈大侠
    链接:https://zhuanlan.zhihu.com/p/35680070
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性、可移植性,代码的可重用性。


    MVC即Model、View、Controller即模型、视图、控制器。我在和同行讨论技术,阅读别人的代码时发现,很多程序员倾向于将软件的业务逻辑放在Controller里,将数据库访问操作的代码放在Model里。


    最终软件(网站)的代码结构是,View层是界面,Controller层是业务逻辑,Model层是数据库访问。


    不知道大家知不知道另外一种软件开发模式三层架构,它和MVC相似之处是也分为三层,分别是UI层表示用户界面,BLL层表示业务逻辑,DAL层表示数据访问。三层架构曾经红极一时,MVC大行其道之后它就销声匿迹了, 可现在看来, 它似乎只是改头换面, 装扮成MVC的样子,并且深受程序员们的欢迎,因为它的这种分层方式和前文描述的MVC如出一辙。


    再说的直白点,很多程序员将MVC当成了三层架构在用,这看起来似乎没什么问题,毕竟三层架构也是一种和MVC齐名的架构模式。可问题在于用三成架构的思路写MVC,那么写出来的东西既不是三成架构也不是MVC,到是像一个什么都不是四不像。熟悉天龙八部的同学应该知道这样一段情节,吐蕃番僧鸠摩智强行用道家的小无相功为基础修炼少林的七十二绝技和易筋经最终导致走火入魔。其实用这个例子来形容现在一些程序员用三层架构的思想写MVC最恰当不过了,三层架构的核心思想是面向接口编程和各层之间的解耦和可替换性,MVC框架中没有这种概念,因为MVC要面对的问题本就不是三成架构要面对的问题,所以以MVC为基础写出来的三成架构是不会具备三层架构的核心要义的,换言之,这种代码是放弃了三层架构和MVC的精华,获得了它们的糟粕,是愚蠢的编码方式。


    我吐槽了这么多,对于吐槽的理由要是说不出个所以然来,估计要被人喷死,下面就来说说MVC本质原理和正确使用方式,当然,这里的MVC指的最纯粹MVC,适合各类软件,而不仅仅指Web框架中的变体MVC,然而万变不离其宗,文中所述的MVC思想同样适用于Web开发。


    MVC要实现的目标是将软件用户界面和业务逻辑分离以使代码可扩展性、可复用性、可维护性、灵活性加强。


    View层是界面,Model层是业务逻辑,Controller层用来调度View层和Model层,将用户界面和业务逻辑合理的组织在一起,起粘合剂的效果。所以Controller中的内容能少则少,这样才能提供最大的灵活性。


    比方说,有一个View会提交数据给Model进行处理以实现具体的行为,View通常不会直接提交数据给Model,它会先把数据提交给Controller,然后Controller再将数据转发给Model。假如此时程序业务逻辑的处理方式有变化,那么只需要在Controller中将原来的Model换成新实现的Model就可以了,控制器的作用就是这么简单, 用来将不同的View和不同的Model组织在一起,顺便替双方传递消息,仅此而已。


    合理的使用MVC有很多好处,要一一道尽是一件异常困难的任务。在这里我们通过一个反面示例来侧面的证明正确使用MVC的好处与依据。


    如前文所言, 很多程序员偏爱于将业务逻辑放在Controller中,我极力反对这种做法,现在我就来证明这中做法的错误性。


    我们知道在写程序时,业务逻辑的重复使用是经常要面对的场景。 如果业务逻辑写在控制器中, 要重用它的唯一方法就是将它提升到父类之中,通过继承来达到代码复用的效果。但这么做会带来一个巨大的副作用,违背了一项重要的面向对象设计原则:接口隔离。


    什么是接口隔离,我在这里简单的讲述一下。通俗一点讲,接口隔离就是当一个类需要继承另一个类时, 如果被继承的类中有继承的类用不到的方法或者属性时,就不要去实现这个继承。如果真的情非得已必须要继承,那么也需要从被继承的类中再提取出一个只包含需要部分功能的新类型,最终去继承这个新类型才是正确的做法。 换句话说,实现继承的时候,不要去继承那些用不到的事物。


    回到之前的话题,通过继承父控制器的方式复用业务逻辑时,往往会出现为了重用一个方法而继承来一大堆用不到的方法,表面上看起来似乎没什么问题,但是这会使代码变的难以理解,
    长此以往, 软件的代码会朝着不健康的方向发展。


    要知道,使用继承的条件是很苛刻的,我们学习面向对象变编程继承特性时,第一课就是只有满足IS-A(是一个)关系时才可以使用继承,如果仅仅是复用代码,并不是我们使用继承的理由。使用组合是复用代码提倡的方式,也就是所谓的HAS-A(有一个)的关系,相信每个程序员都听过“少用继承,多有组合”这句话,这句话是软件开发业的先驱们千锤百炼总结出来的,值得我们去遵循。


    各Model之间是可以相互调用的, Controller也可以无障碍的调用Model,因此将业务逻辑放在Model中可以灵活的使用组合的方式复用代码。


    而Controller之间是不可以相互调用的,要复用代码只能将代码提升至父类,通过继承实现,显然这种做法既不正确,也不灵活,因此完全不提倡。


    综上所述,仅仅只是代码复用这一点,也足以将“厚Controller,薄Model”这种不健康的MVC思想打入十八层地狱。


    现在我们大概知道了代码应该如何分布于MVC三层之间, 知其然,并且也知其所以然。接下来我们再从另一个角度深刻剖析MVC,脱它个精光,让它赤条条展示在我们眼前。


    众所周知,GoF总结过23个设计模式,这23个设计模式是某些特定的编程问题的特效药,这是业内公认的。


    MVC是一种模式,但却在GoF总结出来的这个23个设计模式之外,确切的说它不是一种设计模式,它是多种设计模式的组合,并不仅仅只是一个单独的一个模式。


    组成MVC的三个模式分别是组合模式、策咯模式、观察者模式,MVC在软件开发中发挥的威力,最终离不开这三个模式的默契配合。 那些崇尚设计模式无用论的程序员,请了解只要你们使用MVC,就离不开设计模式。


    注意,以下内容以这三个设计模式的知识为基础,如果对这三个设计模式没概念,或许会阅读困难。


    先说组合模式在MVC中扮演什么样的角色。


    组合模式只在视图层活动, 视图层的实现用的就是组合模式,当然,这里指的实现是底层的实现,是由编程框架厂商做的事情,用不着普通程序员插手。


    组合模式的类层次结构是树状的, 而我们做Web时视图层是html页面,html的结构不正是树状的吗,这其实就是一个组合模式的应用,只是浏览器厂商已经把界面相关的工作帮我们做掉了,但它确确实实是我们应用MVC的其中一部分,只是我们感觉不到罢了,这也是我们觉得View是实现起来最简单最没有歧义的一层的原因。


    除网页以外的其他用户界面程序,如WPF、Android、ASP.NET等等都是使用树状结构来组织界面控件对象的,因为组合模式就是从界面设计的通用解决方案总提炼出来的。所以与其说MVC选择了组合模式,还不如说组合模式是必定会存在MVC中的,因为只要涉及到用户界面,组合模式就必定存。事实上即使不理解组合模式,也不影响程序员正确的使用MVC,组合模式本就存在于程序员接触不到的位置。


    然而,观察者模式和策略模式就显得比较重要,是实实在在MVC中接触的到的部分。


    观察者模式有两部分组成,被观察的对象和观察者,观察者也被称为监听者。对应到MVC中,Model是被观察的对象,View是观察者,Model层一旦发生变化,View层即被通知更新。View层和Model层互相之间是持有引用的。 我们在开发Web MVC程序时,因为视图层的html和Model层的业务逻辑之间隔了一个http,所以不能显示的进行关联,但是他们观察者和收听者的关系却没有改变。当View通过http提交数据给服务器,服务器上的Model接受到数据执行某些操作,再通过http响应将结果回送给View,View(浏览器)接受到数据更新界面,这不正是一个接受到通知并执行更新的行为吗,是观察者模式的另一种表现形式。


    但是,脱离Web,当通过代码去纯粹的表示一个MVC结构的时候,View和Model间无疑是观察者和被观察的关系,是以观察者模式为理论基础的。即使在Web中因为http壁垒的原因导致真正的实现有点走样,但是原理核心和思路哲学却是不变的。


    最后是策略模式。策略模式是View和Controller之间的关系,Controller是View的一个策略,Controller对于View是可替换的, View和Controller的关系是一对多,在实际的开发场景中,也经常会碰到一个View被多个Controller引用,这即使策咯模式的一种体现,只是不那么直观而已。


    总结一下,关于MVC各层之间关系所对应的设计模式


    View层,单独实现了组合模式

    Model层和View层,实现了观察者模式

    View层和Controller层,实现了策咯模式


    MVC就是将这三个设计模式在一起用了,将这三个设计模式弄明白,MVC将毫无神秘感可言。如果不了解这三个设计模式去学习MVC,那不管怎么学总归是一知半解,用的时候也难免不会出想问题。


    再次回到最前面讨论的业务逻辑应该放在Controller还是Model的问题上,从设计模式的角度讲,策略模式中的策略通常都很小很薄,不会包含太多的内容, Controller是一个策略, 自然不应该在里面放置过多的内容,否则要替换一个新的会相当麻烦,与此同时也会破坏View-Model的观察者模式,仿佛View-Controller之间即实现了策略模式又实现了观察者模式,这种混乱是罪恶的根源,是制造焦油坑让程序员陷入其中无法自拔的罪魁祸首。切忌,应当避免

    迫不及待想学习java进阶的加群:617912068



    MVC无人不知,可很多程序员对MVC的概念的理解似乎有误,换言之他们一直在错用MVC,尽管即使如此软件也能被写出来,然而软件内部代码的组织方式却是不科学的,这会影响到软件的可维护性、可移植性,代码的可重用性。


    MVC即Model、View、Controller即模型、视图、控制器。我在和同行讨论技术,阅读别人的代码时发现,很多程序员倾向于将软件的业务逻辑放在Controller里,将数据库访问操作的代码放在Model里。


    最终软件(网站)的代码结构是,View层是界面,Controller层是业务逻辑,Model层是数据库访问。


    不知道大家知不知道另外一种软件开发模式三层架构,它和MVC相似之处是也分为三层,分别是UI层表示用户界面,BLL层表示业务逻辑,DAL层表示数据访问。三层架构曾经红极一时,MVC大行其道之后它就销声匿迹了, 可现在看来, 它似乎只是改头换面, 装扮成MVC的样子,并且深受程序员们的欢迎,因为它的这种分层方式和前文描述的MVC如出一辙。


    再说的直白点,很多程序员将MVC当成了三层架构在用,这看起来似乎没什么问题,毕竟三层架构也是一种和MVC齐名的架构模式。可问题在于用三成架构的思路写MVC,那么写出来的东西既不是三成架构也不是MVC,到是像一个什么都不是四不像。熟悉天龙八部的同学应该知道这样一段情节,吐蕃番僧鸠摩智强行用道家的小无相功为基础修炼少林的七十二绝技和易筋经最终导致走火入魔。其实用这个例子来形容现在一些程序员用三层架构的思想写MVC最恰当不过了,三层架构的核心思想是面向接口编程和各层之间的解耦和可替换性,MVC框架中没有这种概念,因为MVC要面对的问题本就不是三成架构要面对的问题,所以以MVC为基础写出来的三成架构是不会具备三层架构的核心要义的,换言之,这种代码是放弃了三层架构和MVC的精华,获得了它们的糟粕,是愚蠢的编码方式。


    我吐槽了这么多,对于吐槽的理由要是说不出个所以然来,估计要被人喷死,下面就来说说MVC本质原理和正确使用方式,当然,这里的MVC指的最纯粹MVC,适合各类软件,而不仅仅指Web框架中的变体MVC,然而万变不离其宗,文中所述的MVC思想同样适用于Web开发。


    MVC要实现的目标是将软件用户界面和业务逻辑分离以使代码可扩展性、可复用性、可维护性、灵活性加强。


    View层是界面,Model层是业务逻辑,Controller层用来调度View层和Model层,将用户界面和业务逻辑合理的组织在一起,起粘合剂的效果。所以Controller中的内容能少则少,这样才能提供最大的灵活性。


    比方说,有一个View会提交数据给Model进行处理以实现具体的行为,View通常不会直接提交数据给Model,它会先把数据提交给Controller,然后Controller再将数据转发给Model。假如此时程序业务逻辑的处理方式有变化,那么只需要在Controller中将原来的Model换成新实现的Model就可以了,控制器的作用就是这么简单, 用来将不同的View和不同的Model组织在一起,顺便替双方传递消息,仅此而已。


    合理的使用MVC有很多好处,要一一道尽是一件异常困难的任务。在这里我们通过一个反面示例来侧面的证明正确使用MVC的好处与依据。


    如前文所言, 很多程序员偏爱于将业务逻辑放在Controller中,我极力反对这种做法,现在我就来证明这中做法的错误性。


    我们知道在写程序时,业务逻辑的重复使用是经常要面对的场景。 如果业务逻辑写在控制器中, 要重用它的唯一方法就是将它提升到父类之中,通过继承来达到代码复用的效果。但这么做会带来一个巨大的副作用,违背了一项重要的面向对象设计原则:接口隔离。


    什么是接口隔离,我在这里简单的讲述一下。通俗一点讲,接口隔离就是当一个类需要继承另一个类时, 如果被继承的类中有继承的类用不到的方法或者属性时,就不要去实现这个继承。如果真的情非得已必须要继承,那么也需要从被继承的类中再提取出一个只包含需要部分功能的新类型,最终去继承这个新类型才是正确的做法。 换句话说,实现继承的时候,不要去继承那些用不到的事物。


    回到之前的话题,通过继承父控制器的方式复用业务逻辑时,往往会出现为了重用一个方法而继承来一大堆用不到的方法,表面上看起来似乎没什么问题,但是这会使代码变的难以理解,
    长此以往, 软件的代码会朝着不健康的方向发展。


    要知道,使用继承的条件是很苛刻的,我们学习面向对象变编程继承特性时,第一课就是只有满足IS-A(是一个)关系时才可以使用继承,如果仅仅是复用代码,并不是我们使用继承的理由。使用组合是复用代码提倡的方式,也就是所谓的HAS-A(有一个)的关系,相信每个程序员都听过“少用继承,多有组合”这句话,这句话是软件开发业的先驱们千锤百炼总结出来的,值得我们去遵循。


    各Model之间是可以相互调用的, Controller也可以无障碍的调用Model,因此将业务逻辑放在Model中可以灵活的使用组合的方式复用代码。


    而Controller之间是不可以相互调用的,要复用代码只能将代码提升至父类,通过继承实现,显然这种做法既不正确,也不灵活,因此完全不提倡。


    综上所述,仅仅只是代码复用这一点,也足以将“厚Controller,薄Model”这种不健康的MVC思想打入十八层地狱。


    现在我们大概知道了代码应该如何分布于MVC三层之间, 知其然,并且也知其所以然。接下来我们再从另一个角度深刻剖析MVC,脱它个精光,让它赤条条展示在我们眼前。


    众所周知,GoF总结过23个设计模式,这23个设计模式是某些特定的编程问题的特效药,这是业内公认的。


    MVC是一种模式,但却在GoF总结出来的这个23个设计模式之外,确切的说它不是一种设计模式,它是多种设计模式的组合,并不仅仅只是一个单独的一个模式。


    组成MVC的三个模式分别是组合模式、策咯模式、观察者模式,MVC在软件开发中发挥的威力,最终离不开这三个模式的默契配合。 那些崇尚设计模式无用论的程序员,请了解只要你们使用MVC,就离不开设计模式。


    注意,以下内容以这三个设计模式的知识为基础,如果对这三个设计模式没概念,或许会阅读困难。


    先说组合模式在MVC中扮演什么样的角色。


    组合模式只在视图层活动, 视图层的实现用的就是组合模式,当然,这里指的实现是底层的实现,是由编程框架厂商做的事情,用不着普通程序员插手。


    组合模式的类层次结构是树状的, 而我们做Web时视图层是html页面,html的结构不正是树状的吗,这其实就是一个组合模式的应用,只是浏览器厂商已经把界面相关的工作帮我们做掉了,但它确确实实是我们应用MVC的其中一部分,只是我们感觉不到罢了,这也是我们觉得View是实现起来最简单最没有歧义的一层的原因。


    除网页以外的其他用户界面程序,如WPF、Android、ASP.NET等等都是使用树状结构来组织界面控件对象的,因为组合模式就是从界面设计的通用解决方案总提炼出来的。所以与其说MVC选择了组合模式,还不如说组合模式是必定会存在MVC中的,因为只要涉及到用户界面,组合模式就必定存。事实上即使不理解组合模式,也不影响程序员正确的使用MVC,组合模式本就存在于程序员接触不到的位置。


    然而,观察者模式和策略模式就显得比较重要,是实实在在MVC中接触的到的部分。


    观察者模式有两部分组成,被观察的对象和观察者,观察者也被称为监听者。对应到MVC中,Model是被观察的对象,View是观察者,Model层一旦发生变化,View层即被通知更新。View层和Model层互相之间是持有引用的。 我们在开发Web MVC程序时,因为视图层的html和Model层的业务逻辑之间隔了一个http,所以不能显示的进行关联,但是他们观察者和收听者的关系却没有改变。当View通过http提交数据给服务器,服务器上的Model接受到数据执行某些操作,再通过http响应将结果回送给View,View(浏览器)接受到数据更新界面,这不正是一个接受到通知并执行更新的行为吗,是观察者模式的另一种表现形式。


    但是,脱离Web,当通过代码去纯粹的表示一个MVC结构的时候,View和Model间无疑是观察者和被观察的关系,是以观察者模式为理论基础的。即使在Web中因为http壁垒的原因导致真正的实现有点走样,但是原理核心和思路哲学却是不变的。


    最后是策略模式。策略模式是View和Controller之间的关系,Controller是View的一个策略,Controller对于View是可替换的, View和Controller的关系是一对多,在实际的开发场景中,也经常会碰到一个View被多个Controller引用,这即使策咯模式的一种体现,只是不那么直观而已。


    总结一下,关于MVC各层之间关系所对应的设计模式


    View层,单独实现了组合模式

    Model层和View层,实现了观察者模式

    View层和Controller层,实现了策咯模式


    MVC就是将这三个设计模式在一起用了,将这三个设计模式弄明白,MVC将毫无神秘感可言。如果不了解这三个设计模式去学习MVC,那不管怎么学总归是一知半解,用的时候也难免不会出想问题。


    再次回到最前面讨论的业务逻辑应该放在Controller还是Model的问题上,从设计模式的角度讲,策略模式中的策略通常都很小很薄,不会包含太多的内容, Controller是一个策略, 自然不应该在里面放置过多的内容,否则要替换一个新的会相当麻烦,与此同时也会破坏View-Model的观察者模式,仿佛View-Controller之间即实现了策略模式又实现了观察者模式,这种混乱是罪恶的根源,是制造焦油坑让程序员陷入其中无法自拔的罪魁祸首。切忌,应当避免。


    展开全文
  • MVC框架的原理详解

    万次阅读 2017-05-26 18:02:11
    一、MVC原理解析 1、MVC原理 二、HttpHandler 1、HttpHandler、IHttpHandler、MvcHandler的说明2、IHttpHandler解析3、MvcHandler解析 三、HttpModule 1、HttpModule能干什么2、HttpModule的使用3、HttpModule和...
    
    
    

     

    正文

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎。这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之处,还希望大家斧正,博主感激不尽!

    本文原创地址:http://www.cnblogs.com/landeanfen/p/5989092.html

    MVC源码学习系列文章目录:

    一、MVC原理解析

     最近园子里Asp.Net Core火了一阵,不管微软的开源动作有多么迟缓,还是希望微软能够给力一次。作为Core的主要Web框架——MVC,虽然已经开源,但是读起来着实费劲,并且感觉很多核心部件都找不到。于是只能通过Reflector去反编译MVC5的组件以及参考博客园Fish Li等大神的文章去学习下MVC5的原理。

    10月26日更新:感谢园友Adming在评论中提醒,原来Asp.net Core Mvc和Asp.net Mvc 5的原理已经完全不同,难怪在Core Mvc的源码里面已经找不到MvcHandler、UrlRoutingModule等核心部件了呢,此系列文章就先学习下MVC5的原理,等以后有空了再来研究Core MVC吧。

    Asp.Net Core MVC的开源地址:https://github.com/aspnet/Mvc

    Asp.net MVC的开源地址:http://aspnetwebstack.codeplex.com/SourceControl/latest

    1、MVC原理

    之前的文章有介绍MVC的路由机制,其实路由机制算是MVC的原理的核心之一。在此我们还是要不厌其烦再来谈谈整个过程,因为这是理解MVC原理不可逾越的鸿沟。当我们收到一个URL的请求时,服务端收到请求,主要经历以下几个步骤:

    1. 请求被UrlRoutingModule部件拦截
    2. 封装请求上下文HttpContext,成为HttpContextWrapper对象。
    3. 根据当前的HttpContext,从Routes集合中得到与当前请求URL相符合的RouteData对象。
    4. RouteDataHttpContext请求封装成一个RequestContext对象。
    5. 根据RequestContext对象,从RouteData的RouteHandler中获取IHttpHandler(MVC里面会有一个IHttpHandler的实现类MvcHandler)。
    6. 执行IHttpHandler(MvcHandler),然后就是通过反射激活具体的controller,执行具体的action。

    附上一张大致的流程图:

    纵观整个过程,看上去很复杂,各种对象缠绕,看得人晕晕的。其实如果你静下心来仔细研读MVC的源码你会发现其实并没有想像中的那般复杂,请有点耐心听博主慢慢道来。

    1、整个过程有两个核心的组件,文中博主用红色标记了出来:UrlRoutingModuleMvcHandler,上文提到的各个过程都和两个组件有紧密的联系。而这两个组件分别继承至IHttpModule和IHttpHandler接口,熟悉Asp.net管线事件的朋友应该会记得这两个接口,在管道事件里面这两个接口扮演着重要角色。要理解MVC的上述原理,必须要先理解这两类接口的原理以及使用。

    2、UrlRoutingModule的作用可以理解为通过一系列的与路由相关的组件去解析当前请求的Controller与Action名称,其实简单点理解,比如我们请求http://localhost:8080/Home/Index这个url的时候,UrlRoutingModule拦截到这个请求,然后通过一系列的方式得到这里的“Home”和“Index”,这样理解有没有简单一点呢。

    3、MvcHandler的作用就更加直接,上述通过拦截组件得到了请求的Controller和Action的名称,MvcHandler组件将当前请求的Controller名称反射得到对应的控制器对象,然后执行对应的Action方法。比如还是上述http://localhost:8080/Home/Index这个请求,通过字符串“Home”反射成为Home这个类型的控制器对象,然后调用这个对象的Index()方法。

    4、综上,联合这两个组件来理解,UrlRoutingMudule组件的主要作用是解析当前的Controller与Action名称,MvcHandler的作用是将得到的Controller名称激活,得到具体的Controller对象,然后执行对应的Action方法。

    所以,要理解MVC的原理,必须要了解这两个组件的基本原理以及作用。下面就根据这两个组件分别展开说明,相信理解了下面的内容,你对mvc的原理会有一个新的认识。

    二、HttpHandler

    上文说过MvcHandler是继承至IHttpHandler接口的!为什么这里大标题会用HttpHandler而不是MvcHandler呢?因为博主觉得,HttpHandler实在是太重要了,首先得理解了HttpHandler这么一个大的东西,然后再来看具体的MvcHandler才有意义。

    1、HttpHandler、IHttpHandler、MvcHandler的说明

    • HttpHandler指所有实现IHttpHandler接口一类类型的统称,它是一个大的称谓。这些类型有一个共同的功能,那就是可以用来处理Http请求。
    • IHttpHandler是微软定义的一类接口,用来约束所有能够处理Http请求的类型的接口规则。
    • MvcHandler是Mvc里面实现IHttpHandler接口的类型,也就是说,MvcHandler是Mvc里面处理Http请求的类型。

    总而言之,HttpHandler只是一个逻辑称谓,它并不具体存在。而IHttpHandler和MvcHandler是.net framework里面具体存在的接口和实现类,是前者的表现形式。

    2、IHttpHandler解析

     2.1、Asp.net管线事件简易说明

    做过Webform开发的园友应该记得,在asp.net的页面生命周期里面,一共有24个管线事件,完整的管线事件可参考MSDN文档:

     
    在处理该请求时将由 HttpApplication 类执行以下事件。 希望扩展 HttpApplication 类的开发人员尤其需要注意这些事件。
    1. 对请求进行验证,将检查浏览器发送的信息,并确定其是否包含潜在恶意标记。 有关更多信息,请参见 ValidateRequest 和脚本侵入概述。
    2. 如果已在 Web.config 文件的 UrlMappingsSection 节中配置了任何 URL,则执行 URL 映射。
    3. 引发 BeginRequest 事件。
    4. 引发 AuthenticateRequest 事件。
    5. 引发 PostAuthenticateRequest 事件。
    6. 引发 AuthorizeRequest 事件。
    7. 引发 PostAuthorizeRequest 事件。
    8. 引发 ResolveRequestCache 事件。
    9. 引发 PostResolveRequestCache 事件。
    10. 根据所请求资源的文件扩展名(在应用程序的配置文件中映射),选择实现 IHttpHandler 的类,对请求进行处理。 如果该请求针对从 Page 类派生的对象(页),并且需要对该页进行编译,则 ASP.NET 会在创建该页的实例之前对其进行编译。
    11. 引发 PostMapRequestHandler 事件。
    12. 引发 AcquireRequestState 事件。
    13. 引发 PostAcquireRequestState 事件。
    14. 引发 PreRequestHandlerExecute 事件。
    15. 为该请求调用合适的 IHttpHandler 类的 ProcessRequest 方法(或异步版 IHttpAsyncHandler.BeginProcessRequest)。 例如,如果该请求针对某页,则当前的页实例将处理该请求。 
    16. 引发 PostRequestHandlerExecute 事件。
    17. 引发 ReleaseRequestState 事件。
    18. 引发 PostReleaseRequestState 事件。
    19. 如果定义了 Filter 属性,则执行响应筛选。
    20. 引发 UpdateRequestCache 事件。
    21. 引发 PostUpdateRequestCache 事件。
    22. 引发 EndRequest 事件。
    23. 引发 PreSendRequestHeaders 事件。
    24. 引发 PreSendRequestContent 事件。
     

    这里不可能把每个管线事件将清楚,但是在整个管线事件中,有两个重要的角色就是HttpHandlerHttpModule。在这些事件中,第10个事件【根据所请求资源的文件扩展名(在应用程序的配置文件中映射),选择实现 IHttpHandler 的类,对请求进行处理】 是HttpHandler创建的地方。关于WebForm里面HttpHandler创建的详细过程,这里就不展开说了,如果有兴趣可以参考http://www.cnblogs.com/fish-li/archive/2012/01/29/2331477.html

    2.2、Asp.net中常见的HttpHandler类型

    首先还是来看看IHttpHandler的定义

    public interface IHttpHandler
    {
        // 定义一个处理当前http请求的方法
        void ProcessRequest(HttpContext context);
    
        // 指示当前实例是否可以再次使用
        bool IsReusable { get; }
    }
    
     
     

    接口的定义很简单,ProcessRequest()方法里面传一个当前请求的上下文对象去处理当前的http请求。

    为了处理异步请求,Framework里面还定义了一个异步的IHttpHandler接口:

     
    public interface IHttpAsyncHandler : IHttpHandler
    {
        // Methods
        IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
        void EndProcessRequest(IAsyncResult result);
    }

    接口的两个方法应该也不难理解。

    我们已经说了,HttpHandler的主要作用是处理http请求,原来在做webform的时候应该都写过后缀ashx的一般处理程序吧,这个一般处理程序就是通过实现IHttpHandler接口去实现的。我们是否曾经也写过类似这样的代码,新建一个TestHttpHandler.ashx文件,代码如下:

     
    public class TestHttpHandler : IHttpHandler
        {
    
            public void ProcessRequest(HttpContext context)
            {
                context.Response.ContentType = "text/plain";
    
                var username = context.Request.QueryString["username"];
                var password = context.Request.QueryString["password"];
                if (username == "admin" && password == "admin")
                {
                    context.Response.Write("用户admin登录成功");
                }
                else
                {
                    context.Response.Write("用户名或者密码错误");
                }
            }
    
            public bool IsReusable
            {
                get
                {
                    return false;
                }
            }
        }
     

    然后运行,通过http://localhost:16792/TestHttpHandler.ashx?username=admin&password=admin去访问一般处理程序,即可得到正确的结果。

    当然,除了这个,还有我们最常见的aspx页面。

     
        public partial class TestPage : System.Web.UI.Page
        {
            protected void Page_Load(object sender, EventArgs e)
            {
    
            }
        }

    将Page类转到定义:

    发现原来Page类也是继承至IHttpHandler,这就是为什么我们可以通过地址http://localhost:16792/TestPage.aspx来访问这个页面的原因。当然,子类中的ProcessRequest()方法并没有显示的声明出来,因为在Page类里面已经有一个virtue的虚方法,如果需要,你也可以在TestPage这个类里面显示声明:

        public partial class TestPage : System.Web.UI.Page
        {
            protected void Page_Load(object sender, EventArgs e)
            {
    
            }
    
            public void ProcessRequest(HttpContext context)
            {
                context.Response.Write("你好");
            }
        }

    然后你会发现这个时候请求会进到ProcessRequest()方法,而不会进到Page_Load()里面了,至于原因,这和Page类里面的封装有关系。当然这不是本文的重点,本文要说明的是所有实现了IHttpHandler接口的类型都可以在ProcessRequest()方法里面处理当前http请求。

    当然,除了ashx和aspx以外,还有一类http的服务接口处理文件asmx也和IHttpHandler有着不可分割的联系,可以说,在asp.net里面,只要是处理Http请求的地方,IHttpHandler几乎“无处不在”。

    2.3、自定义HttpHandler。

    当然,除了上述asp.net自带的HttpHandler之外,我们也可以自定义HttpHandler处理特定的请求。比如我们新建一个TestMyHandler.cs页面:

        public class TestMyHandler:IHttpHandler
        {
            public bool IsReusable
            {
                get { return false; }
            }
    
            public void ProcessRequest(HttpContext context)
            {
                context.Response.Write("从asex页面进来");
    
                //throw new NotImplementedException();
            }
        }

    当然,要使用这个自定义的Handler需要在web.config里面加上配置。(PS:这部分是博主后来加上的,所以直接用正确的配置)

    <system.webServer>
       <handlers>
            <add name="asex" verb="*" path="*.asex" type="MyTestMVC.TestMyHandler, MyTestMVC" preCondition="integratedMode" />
        </handlers>
    </system.webServer>

    这个配置的意思是所有的url以asex结尾的请求都交给TestMyHandler这个类去处理。得到效果:

     

    3、MvcHandler解析

    上文介绍了那么多IHttpHandler的用法,都是在WebForm里面的一些实现,我们知道了所有实现了IHttpHandler的类都可以处理Http请求。同样在MVC里面,也定义了一个实现IHttpHandler接口的类型——MvcHandler,用于处理当前的http请求。通过反编译工具可以看到:

    public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
    {
        // 省略若干字段// 所有方法
        static MvcHandler();
        public MvcHandler(RequestContext requestContext);
        protected internal virtual void AddVersionHeader(HttpContextBase httpContext);
        protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state);
        protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state);
        protected internal virtual void EndProcessRequest(IAsyncResult asyncResult);
        private static string GetMvcVersionString();
        protected virtual void ProcessRequest(HttpContext httpContext);
        protected internal virtual void ProcessRequest(HttpContextBase httpContext);
        private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory);
        private void RemoveOptionalRoutingParameters();
        IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
        void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result);
        void IHttpHandler.ProcessRequest(HttpContext httpContext);
    
        // 省略若干属性
    }

    MvcHandler实现了IHttpHandler、 IHttpAsyncHandler两个接口,异步请求这里先不做介绍。重点还是来看看ProcessRequest()方法

    将HttpContext转换为HttpContextBase对象,继续转到定义。

    这里声明了一个IController和IControllerFactory对象,通过this.ProcessRequestInit()方法创建具体的Controller实例。我们将ProcessRequestInit()方法转到定义

    我们将代码复制出来,写入相应的注释:

     
         private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
            {
                //1.得到当前的上下文
                HttpContext current = HttpContext.Current;
                if (current != null && ValidationUtility.IsValidationEnabled(current) == true) ValidationUtility.EnableDynamicValidation(current);
                this.AddVersionHeader(httpContext);
                this.RemoveOptionalRoutingParameters();
    
                //2.从路由对象RouteData中获取当前请求的Controller名称
                string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
    
                //3.得到Controller工厂对象
                factory = this.ControllerBuilder.GetControllerFactory();
    
                //4.根据当前RequestContext对象,从Controller工厂创建具体的Controller对象
                controller = factory.CreateController(this.RequestContext, requiredString);
                if (controller == null) throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[] { factory.GetType(), requiredString }));
            }
     

    通过上文的注释很好理解整个控制器的实例化过程。本打算看下Controller工厂如何创建以及控制器如何实例化的,奈何这部分反编译不了。我们暂且理解为反射吧,这些实现细节并不影响我们理解整个过程。

    创建控制器成功之后,就是执行Action方法了,这个过程在上面反编译的第二张图片的 controller.Execute(this.RequestContext); 方法得到体现。所以,除去细节,理解MvcHandler的ProcessRequest()方法并不是太难。

    三、HttpModule

    除了HttpHandler之外,Asp.net里面还有另外一个重要的角色——HttpModule。和HttpHandler类似,HttpModule指所有实现了IHttpModule接口的一类类型的统称。至于HttpModule、IHttpModule、UrlRoutingModule各个名称的含义和上述HttpHandler相同,在此不做重复说明。

    1、HttpModule能干什么

    通过上文,我们知道HttpHandler的作用非常明确:处理Http请求,生成相应结果。那么,HttpModule又是干什么的呢?

    HttpHandler的作用是处理某一类别的请求,比如ashx、aspx、asmx等,在某些情况下,各类请求可能都需要进行某些相同的处理(比如请求拦截、身份认证、检查功能等),不可能在每个类别的HttpHandler里面都去实现这些相同的代码,这个时候怎么办呢?处理某一类通用请求,提高代码的复用率。是不是想到了我们的面向切面编程(AOP),没错,HttpModule就是负责做这个事,HttpModule通过事件订阅的方式,将某类HttpHandler都需要的功能抽取出来,这些功能可以编译成类库供各个模块调用。这种采用事件(观察者)的设计模式使得系统设计上更加灵活。

    2、HttpModule的使用

    先来看看IHttpModule的定义

    public interface IHttpModule
    {
        //初始化
        void Init(HttpApplication context);
       
        //释放
        void Dispose();
    }

    接口定义很简单,一个初始化组件的方法,一个释放对象的方法。

    我们来写一个测试的例子具体看看HttpModule如何注册事件,我们新建一个IHttpModule的实现类:

    namespace MyTestMVC
    {
        public class TestMyModule:IHttpModule
        {
            public void Dispose()
            {
                //throw new NotImplementedException();
            }
    
            public void Init(HttpApplication app)
            {
                //事件注册
                app.BeginRequest += app_BeginRequest;
                app.EndRequest += app_EndRequest;
            }
    
            void app_EndRequest(object sender, EventArgs e)
            {
                var app = (HttpApplication)sender;
                app.Context.Response.Write("请求结束");
            }
    
            void app_BeginRequest(object sender, EventArgs e)
            {
                var app = (HttpApplication)sender;
                app.Context.Response.Write("请求开始");
            }
        }
    }
     

    在Init方法里面,通过HttpApplication对象来注册请求的事件。这样,每次发起一次http请求的时候都进到这两个方法。

    当然,这些注册就能执行了吗?想得美,系统哪里知道你这个自定义HttpModule的存在,所以必须要在Web.config里面声明一下。

     <system.web>
        <httpModules>
            <add name="TestMyModule" type="MyTestMVC.TestMyModule, MyTestMVC" />
        </httpModules>
      </system.web>

    出现结果:

    查阅资料后发现,原来IIS经典模式下必须要这样配置:

    <system.webServer>
        <modules>
            <add name="TestMyModule" type="MyTestMVC.TestMyModule, MyTestMVC" preCondition="integratedMode" />
        </modules>
    </system.webServer>

    没办法,用微软的东西就要遵守别人的游戏规则。改成这样之后得到结果:

    文中的“你好”来自这里:

     既然HttpModule是事件注册机制的,那么如果需要在同一个事件里面去实现不同的功能,也就是说同一个事件注册多次是否可行呢?我们来试一把:

    假如TestMyModule.cs这个自定义Module的作用是功能检查:

    TestMyModule.cs

    然后新建一个TestMyModule2.cs这个自定义Module,去实现请求拦截的功能:

    TestMyModule2.cs

    最后在Web.config里面配置两个Module:

     
    <system.webServer>
        <modules>
            <add name="TestMyModule" type="MyTestMVC.TestMyModule, MyTestMVC" preCondition="integratedMode" />
            <add name="TestMyModule2" type="MyTestMVC.TestMyModule2, MyTestMVC" preCondition="integratedMode" />
        </modules>
    </system.webServer>
     

    得到结果:

     

    这说明同一个事件可以注册多次,即可以在同一个事件里面做不同的事。

     

    3、HttpModule和HttpHandler如何区分

    通过上文的HttpModule的应用,我们看到在Init方法里面可以拿到当前应用的HttpApplication对象,拿到这个貌似就可以拿到当前请求上下文里面的Request、Response了,是不是就可以处理当前的http请求了,从这点上来说,HttpModule也能处理http请求,或者说具有处理http请求的能力。既然HttpHandler和HttpModule都可以处理http请求,那在使用的时候如何区分呢?上文说过,HttpModule的作用类似AOP,是针对某些通用功能(请求拦截、身份认证、检查功能)的,而HttpHandler常用来处理某一类(ashx、aspx、asmx)http请求,两者的侧重点不同,至于具体在实际中如何使用,你可以自行考量。

    4、UrlRoutingModule解析

    好了,上面介绍那么多HttpModule的使用,都是在为了解Mvc里面的UrlRoutingModule做铺垫。上文说过UrlRoutingModule的作用是拦截请求,那么它是如何做的呢,还是来反编译看看吧。

     
    public class UrlRoutingModule : IHttpModule
    {
        // Fields
        private static readonly object _contextKey;
        private static readonly object _requestDataKey;
        private RouteCollection _routeCollection;
    
        // Methods
        static UrlRoutingModule();
        public UrlRoutingModule();
        protected virtual void Dispose();
        protected virtual void Init(HttpApplication application);
        private void OnApplicationPostResolveRequestCache(object sender, EventArgs e);
        [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
        public virtual void PostMapRequestHandler(HttpContextBase context);
        public virtual void PostResolveRequestCache(HttpContextBase context);
        void IHttpModule.Dispose();
        void IHttpModule.Init(HttpApplication application);
    
        // Properties
        public RouteCollection RouteCollection { get; set; }
    }
     

    重点肯定在Init()方法。

    图一:

    注册HttpApplication对象的PostResolveRequestCache事件。

    图二:

    封装HttpContext,成为HttpContextWrapper对象

    图三:

    这部分代码是我们上述路由理论的代码实践,所以这段代码很重要,我们将代码拷贝出来:

     
         public virtual void PostResolveRequestCache(HttpContextBase context)
            {
                //1.传入当前上下文对象,得到与当前请求匹配的RouteData对象
                RouteData routeData = this.RouteCollection.GetRouteData(context);
                if (routeData != null)
                {
                    //2.从RouteData对象里面得到当前的RouteHandler对象。其实这里的RouteHandler属性对应就是一个MvcRouteHandler的对象。
                    IRouteHandler routeHandler = routeData.RouteHandler;
                    if (routeHandler == null) throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
                    if (!(routeHandler is StopRoutingHandler))
                    {
                        //3.根据HttpContext和RouteData得到RequestContext对象
                        RequestContext requestContext = new RequestContext(context, routeData);
                        context.Request.RequestContext = requestContext;
    
                        //4.根据RequestContext对象得到处理当前请求的HttpHandler(MvcHandler)。
                        IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
                        if (httpHandler == null)
                        {
                            object[] args = new object[] { routeHandler.GetType() };
                            throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), args));
                        }
                        if (httpHandler is UrlAuthFailureHandler)
                        {
                            if (!FormsAuthenticationModule.FormsAuthRequired) throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
                            UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
                        }
                        else
                            //5.请求转到HttpHandler进行处理(进入到ProcessRequest方法)。这一步很重要,由这一步开始,请求才由UrlRoutingModule转到了MvcHandler里面
                            context.RemapHandler(httpHandler);
                    }
                }
            }

    博主在主要的地方加上了注释。

    代码释疑:这里有几点需要说明的。

    1、HttpApplication对象的PostResolveRequestCache事件在MSDN上的解释是:在 ASP.NET 跳过当前事件处理程序的执行并允许缓存模块满足来自缓存的请求时发生。查阅相关资料发现,之所以在PostResolveRequestCache事件注册路由、匹配HttpHandler,是为了满足IIS6。可以参考Tom大叔的文章:http://www.cnblogs.com/TomXu/p/3756858.html

    2、 IRouteHandler routeHandler = routeData.RouteHandler; 这里的routeHandler实际上是一个MvcRouteHandler类型的对象,为什么这么说,我们来反编译下这个就会一目了然:

    图一:

     

    MvcRouteHandler实现了IRouteHandler接口。然后我们重点来看GetHttpHandler()方法得到的是哪个HttpHandler。

    图二:

     

    看到最后一句是不是立马就明白了。也就是说GetHttpHandler()这个方法决定了采用MvcHandler去处理当前的http请求。所以在上述 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 这一句得到的就是一个MvcHandler的实例。

    3、 context.RemapHandler(httpHandler); 这一句可以理解为将当前的请求上下文交给httpHandler这个对象去处理。

    4、到这里,我们再反过来看前面的MVC的原理就完全明朗了。

    1. 请求被UrlRoutingModule部件拦截————通过注册HttpApplication对象的PostResolveRequestCache事件来实现拦截
    2. 封装请求上下文HttpContext,成为HttpContextWrapper对象。————将UrlRoutingModule的Init()方法转到定义,可以看到这么一句: HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context); 
    3. 根据当前的HttpContext,从Routes集合中得到与当前请求URL相符合的RouteData对象。————将UrlRoutingModule的Init()方法转到定义,最终会找到PostResolveRequestCache()方法,方法里面有一句 RouteData routeData = this.RouteCollection.GetRouteData(context); 
    4. RouteDataHttpContext请求封装成一个RequestContext对象。————同样在上述方法里面 RequestContext requestContext = new RequestContext(context, routeData); 
    5. 根据RequestContext对象,从RouteData的RouteHandler中获取IHttpHandler(MVC里面会有一个IHttpHandler的实现类MvcHandler)。————同样在该方法里面 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 
    6. 执行IHttpHandler(MvcHandler)。———— context.RemapHandler(httpHandler); 将请求交给MvcHandler处理。
    7. 然后就是通过反射激活具体的controller,执行具体的action。————在MvcHandler的ProcessRequest()方法里面的执行逻辑

     四、总结

    写到这里,总算把整个过程梳理了一遍,很多细节都未涉及,但是大的过程应该还是明朗的。通篇比较偏理论,所以整体上比较枯燥,但是还是希望园友们能够静下心来慢慢看,因为博主觉得这些对于理解MVC原理太重要!!!想想看,如果你也完全理解了这个过程,是不是都可以自己通过实现IHttphandler和IHttpModule去搭建一个简单的MVC框架了,不错,博主确实是这样打算的,这篇把理论搞清楚,下篇就是实现的细节了。其实写自己的MVC框架更多的在于学习MVC原理,希望自己能够坚持下去。如果你觉得本文能够帮助你,可以右边随意 打赏 博主,也可以 推荐 进行精神鼓励。你的支持是博主继续坚持的不懈动力。

    展开全文
  • MVC模式简介

    万次阅读 多人点赞 2019-01-29 10:02:36
    本文简单介绍 MVC 模式的相关内容。 1 what MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。 MVC ...
  • 详细介绍一下MVC(一)

    2020-05-14 20:46:46
    MVC项目文件夹说明 App_Data:用来保存数据文件,暂时不需关心 App_Start:包含ASP.NET-MVC系统启动的相关类文件 Controller:存放整个项目的“控制器”的代码文件 Models:存放整个项目的“模型”代码文件 Views:...
  • MVC

    2020-05-12 09:51:32
    01.介绍 02.
  • MVC详解

    2018-09-14 17:45:43
    MVC(Model View Controller),是模型(model)-视图(view)-控制器(controller)的缩写,它是一种软件设计思想和架构模式,用来将业务逻辑、数据操作、界面显示分离分别组织代码的一种软件结构设计。该设计思想于...
  • 什么是MVC

    万次阅读 多人点赞 2018-08-26 09:36:20
    MVC的全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种软件设计典范。它是用一种业务逻辑、数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件里面,...
  • MVC、MVP、MVVM,我到底该怎么选?

    万次阅读 多人点赞 2018-07-03 20:42:29
    本文由玉刚说写作平台提供写作赞助 原作者:AndroFarmer 版权声明:本文版权归微信...比如看了好多篇文章都搞不懂MVC到底是个啥本来想写个MVP写着写着就变成MVC了,到底Databing和MVVM之间有啥见不得人的关系...
  • Spring MVC面试题(2020最新版)

    万次阅读 多人点赞 2020-02-19 17:54:38
    文章目录概述什么是Spring MVC?简单介绍下你对Spring MVC的理解?Spring MVC的优点核心组件Spring MVC的主要组件?什么是DispatcherServlet什么是Spring MVC框架的控制器?Spring MVC的控制器是不是单例模式,如果是...
  • MVC框架看MVC架构的设计

    万次阅读 多人点赞 2011-08-16 09:57:37
    MVC框架看MVC架构的设计尽管MVC早已不是什么新鲜话题了,但是从近些年一些优秀MVC框架的设计上,我们还是会发现MVC在架构设计上的一些新亮点。本文将对传统MVC架构中的一些弊病进行解读,了解一些优秀MVC框架是...
  • MVC设计模式和MVC框架的区别

    千次阅读 多人点赞 2017-11-20 09:34:14
    MVC框架和MVC设计模式的区别
  • MVC模式

    千次阅读 2017-09-13 10:45:25
    一、MVC模式简介 MVC是一种架构型模式,它本身并不引入新的功能,只是用来指导我们改善应用程序的架构,使得应用的模型和视图相分离,从而得到更好的开发和维护效率。  在MVC模式中,应用程序被划分成了模型...
  • 【Spring】Spring MVC原理及配置详解

    万次阅读 多人点赞 2016-04-27 10:14:29
    【Spring】Spring MVC原理及配置1.Spring MVC概述:Spring MVC是Spring提供的一个强大而灵活的web框架。借助于注解,Spring MVC提供了几乎是POJO的开发模式,使得控制器的开发和测试更加简单。这些控制器一般不直接...
  • C# ASP.NET MVC WebSocket

    千次下载 热门讨论 2012-10-20 18:03:30
    用ASP.NET MVC C#语言实现WebSocket
  • MVC设计模式

    万次阅读 2009-03-17 09:42:00
    MVC设计模式 1 前言 用户界面,特别是图形用户界面,承担着向用户显示问题模型和与用户进行操作和I/O交互的作用。用户希望保持交互操作界面的相对稳定,但更希望根据需要改变和调整显示的内容和形式。例如,要求...
  • mvc1,mvc2,mvc3都有什么区别

    千次阅读 2011-09-15 11:59:14
    mvc1 模式: view接收用户输入,把命令传到controller controller处理命令,更新model model被更新后,会通知view需要update view更新后向用户显示  mvc2 模式: 由于mvc1中,model可以通知vi
  • Asp.net MVC 与 Asp.net Web API 区别

    千次阅读 2019-07-27 17:00:57
    Asp.net MVC 与 Asp.net Web API 区别 Asp.net MVC 与 Asp.net Web API 区别Asp.Net Web API VS Asp.Net MVC 在我们开发一些web应用时,我们一样可以在MVC Framework 中使用JsonResult 来返回JSON数据,同样也可以...

空空如也

1 2 3 4 5 ... 20
收藏数 641,170
精华内容 256,468
关键字:

mvc