mvc原理_mvc原理和mvc模式的优缺点 - CSDN
精华内容
参与话题
  • 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的工作原理

    万次阅读 2017-10-24 16:42:32
    一、MVC简介 **书面解释:**MVC全名是Model View Controller,是模型-视图-控制器的缩写,一种软件设计典范,是一种业务逻辑和数据显示分离的方法组织代码,将业务逻辑被聚集的一个部件里。在界面和用户围绕数据的...

    一、MVC简介

    **书面解释:**MVC全名是Model View Controller,是模型-视图-控制器的缩写,一种软件设计典范,是一种业务逻辑和数据显示分离的方法组织代码,将业务逻辑被聚集的一个部件里。在界面和用户围绕数据的交互能被改进和个性化定制的同时而不需要重新编写业务逻辑。

    学习MVC的目的:

    1. 快速上手各大主流PHP框架,例如yii框架、Thinkphp、ci等
    2. 快速适应多数公司的web开发需求
    3. 逐步接触和了解web系统架构的知识,为日后成为系统架构的中间力量做准备

    MVC解决的问题:

    在PHP还不支持面向对象之前,过程化的模式来创建。他们将像数据库查询语句这样的数据层代码和像HTML这样的表示层代码混在一起。这样编写的代码程序十分的混乱。

    • 维护难
    • 开发速度慢
    • 二次开发难度高,工作量大

    MVC的优势:

    • 三个层各司其职,互不干扰,有利于开发中的分工;
    • 有利于代码的重用;

    二、MVC的组成与原理

    MVC全名是Model View Controller, 是模型-视图-控制器的缩写。

    • 视图:我们能看到的web界面。
    • 控制器:向系统发出指令的工具和帮手。
    • 模型:模型的工作是按要求从数据库取出数据,当然这个解释还不全面,后面再逐步理解。


    三、MVC工作流程

    • 第一步:浏览者 -> 调用控制器,对它发出指令
    • 第二步:控制器 -> 按指令选取一个合适的模型
    • 第三步:模型 -> 按控制器指令取相应数据
    • 第四步:控制器 -> 按指令选取相应的视图
    • 第五步:视图 -> 把第三步取到的数据按用户想要的样子显示出来







    展开全文
  • MVC架构原理

    2018-08-31 22:04:10
    MVC是一种架构设计模式,是一种设计理念。是为了达到分层设计的目的,从而使代码解耦,便于维护和代码的复用。MVC是3个单词的缩写,全称:Model-View-Controller(模型-视图-控制器)。 举一个例子,MVC就好比我们...

    MVC是一种架构设计模式,是一种设计理念。是为了达到分层设计的目的,从而使代码解耦,便于维护和代码的复用。MVC是3个单词的缩写,全称:Model-View-Controller(模型-视图-控制器)。

    举一个例子,MVC就好比我们的鞋柜。当没有鞋柜的时候,鞋子是这样摆放的:

    有了鞋柜之后,我们的鞋子是这样摆放的:

    一眼就能看出,有了鞋柜之后,鞋子的摆放明显的整齐和有序很多,这样也很方便我们找到自己想穿的鞋子,不用将大量的时间花在寻找鞋子上。如果把我们的成千上万行代码和各种复杂的业务逻辑看作是各式各样的鞋子,那我们的MVC就是鞋柜。MVC让你的代码结构更加清晰明了。

    没有使用MVC的时候,我们的代码结构如下:

    上图那一坨“剪不断、理还乱”的乱麻就是你没有使用分层设计的代码结构。如果这时让你找你代码中的某一段逻辑估计是很费劲的,更别说将代码中的某一段代码进行复用或者替换了。

    使用MVC分层设计之后,我们的代码结构如下:

    上面的图示可能有点夸张,但是这样可能更好的理解。MVC其实就是提供一种规则,让你把相同类型的代码放在一起,这样就形成了层次,从而达到分层解耦、复用、便于测试和维护的目的。

    以上说了一堆,其实就是想让大家理解MVC是什么,有什么作用。接下来,我们结合我们实际开发中的代码类型来解释一下MVC。

    1、Model

    模型层,可以简单理解就是数据层,用于提供数据。在项目中,(简单理解)一般把数据访问和操作,比如将对象关系映射这样的代码作为Model层,也就是对数据库的操作这一些列的代码作为Model层。比如代码中我们会写DAO和DTO类型的代码,那这个DAO和DTO我们可以理解为是属于Model层的代码。

    2、View

    视图层,就是UI界面,用于跟用户进行交互。一般所有的JSP、Html等页面就是View层。

    3、Controller

    控制层,Controller层的功能就是将Model和View层进行关联。比如View主要是显示数据的,但是数据又需要Model去访问,这样的话,View会先告诉Controller,然后Controller再告诉Model,Model请求完数据之后,再告诉View。这样View就可以显示数据了。如下图:

    关于Spring MVC和Struts,与MVC的关系:

    大家还记得在上面我举过的一个例子,MVC好比鞋柜。那Spring和Struts2只是不同牌子的鞋柜而已。并且Spring MVC和Struts2只是一个协助程序员更好实现MVC分层架构的框架而已。就是说,我们实现MVC不一定非要使用Spring或者struts2,自己按照MVC的理解,自己完成自己代码的分层也行。就好比自己在家用木棍自己制作一个鞋柜也照样可以把鞋子摆放整齐,当然,这样做的话首先要你有这样的一个木工技术。我们没有必要为了一个鞋柜,还要自己去学习木工技术,所以最好最快的方式就是去超市买一个鞋柜。

    我们直接使用Spring mvc或者struts2来实现MVC,就是我们直接使用别人做好的东西,直接用。快捷、省时、省事、而且质量好。

     

    最后:

    其实现在除了MVC架构之外,还有MVP、MVVM等。

    在实际项目中MVC更好的实现应该还多一个service层,用来处理业务逻辑。如下:

    其中多出来的Service层,主要是用来处理复杂的业务逻辑,这样结构层次更加鲜明和简介。

    展开全文
  • SpringMVC实现原理及详解

    万次阅读 多人点赞 2018-05-24 11:16:14
    asdfasf

    1、Spring mvc介绍

    SpringMVC框架是以请求为驱动,围绕Servlet设计,将请求发给控制器,然后通过模型对象,分派器来展示请求结果视图。其中核心类是DispatcherServlet,它是一个Servlet,顶层是实现的Servlet接口。


    2、SpringMVC使用

    需要在web.xml中配置DispatcherServlet。并且需要配置spring监听器ContextLoaderListener

            <listener>
                <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
            </listener>        
            <servlet>
    		<servlet-name>springmvc</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
                    <!-- 如果不设置init-param标签,则必须在/WEB-INF/下创建xxx-servlet.xml文件,其中xxx是servlet-name中配置的名称。  -->
                    <init-param>
    			<param-name>contextConfigLocation</param-name>
    			<param-value>classpath:spring/springmvc-servlet.xml</param-value>
    		</init-param>
    		<load-on-startup>1</load-on-startup>
    	</servlet>
    	<servlet-mapping>
    		<servlet-name>springmvc</servlet-name>
    		<url-pattern>/</url-pattern>
    	</servlet-mapping>

    3、SpringMVC运行原理

    如图所示:


    流程说明:

    (1)客户端(浏览器)发送请求,直接请求到DispatcherServlet。

    (2)DispatcherServlet根据请求信息调用HandlerMapping,解析请求对应的Handler。

    (3)解析到对应的Handler后,开始由HandlerAdapter适配器处理。

    (4)HandlerAdapter会根据Handler来调用真正的处理器开处理请求,并处理相应的业务逻辑。

    (5)处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View。

    (6)ViewResolver会根据逻辑View查找实际的View。

    (7)DispaterServlet把返回的Model传给View。

    (8)通过View返回给请求者(浏览器)

    4、DispatcherServlet详细解析

    首先看下源码:

    package org.springframework.web.servlet;
    
    @SuppressWarnings("serial")
    public class DispatcherServlet extends FrameworkServlet {
    
    	public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
    	public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
    	public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
    	public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
    	public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
    	public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
    	public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
    	public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
    	public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
    	public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
    	public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
    	public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
    	public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
    	public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
    	public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";
    	public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";
    	public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";
    	public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
    	private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
    	protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
    	private static final Properties defaultStrategies;
    	static {
    		try {
    			ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
    			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    		}
    		catch (IOException ex) {
    			throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
    		}
    	}
    
    	/** Detect all HandlerMappings or just expect "handlerMapping" bean? */
    	private boolean detectAllHandlerMappings = true;
    
    	/** Detect all HandlerAdapters or just expect "handlerAdapter" bean? */
    	private boolean detectAllHandlerAdapters = true;
    
    	/** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */
    	private boolean detectAllHandlerExceptionResolvers = true;
    
    	/** Detect all ViewResolvers or just expect "viewResolver" bean? */
    	private boolean detectAllViewResolvers = true;
    
    	/** Throw a NoHandlerFoundException if no Handler was found to process this request? **/
    	private boolean throwExceptionIfNoHandlerFound = false;
    
    	/** Perform cleanup of request attributes after include request? */
    	private boolean cleanupAfterInclude = true;
    
    	/** MultipartResolver used by this servlet */
    	private MultipartResolver multipartResolver;
    
    	/** LocaleResolver used by this servlet */
    	private LocaleResolver localeResolver;
    
    	/** ThemeResolver used by this servlet */
    	private ThemeResolver themeResolver;
    
    	/** List of HandlerMappings used by this servlet */
    	private List<HandlerMapping> handlerMappings;
    
    	/** List of HandlerAdapters used by this servlet */
    	private List<HandlerAdapter> handlerAdapters;
    
    	/** List of HandlerExceptionResolvers used by this servlet */
    	private List<HandlerExceptionResolver> handlerExceptionResolvers;
    
    	/** RequestToViewNameTranslator used by this servlet */
    	private RequestToViewNameTranslator viewNameTranslator;
    
    	private FlashMapManager flashMapManager;
    
    	/** List of ViewResolvers used by this servlet */
    	private List<ViewResolver> viewResolvers;
    
    	public DispatcherServlet() {
    		super();
    	}
    
    	public DispatcherServlet(WebApplicationContext webApplicationContext) {
    		super(webApplicationContext);
    	}
    
    	@Override
    	protected void onRefresh(ApplicationContext context) {
    		initStrategies(context);
    	}
    
    	protected void initStrategies(ApplicationContext context) {
    		initMultipartResolver(context);
    		initLocaleResolver(context);
    		initThemeResolver(context);
    		initHandlerMappings(context);
    		initHandlerAdapters(context);
    		initHandlerExceptionResolvers(context);
    		initRequestToViewNameTranslator(context);
    		initViewResolvers(context);
    		initFlashMapManager(context);
    	}
    }
    

    DispatcherServlet类中的属性beans:

    HandlerMapping:用于handlers映射请求和一系列的对于拦截器的前处理和后处理,大部分用@Controller注解。

    HandlerAdapter:帮助DispatcherServlet处理映射请求处理程序的适配器,而不用考虑实际调用的是 哪个处理程序。

    HandlerExceptionResolver:处理映射异常。

    ViewResolver:根据实际配置解析实际的View类型。

    LocaleResolver:解决客户正在使用的区域设置以及可能的时区,以便能够提供国际化视野。

    ThemeResolver:解决Web应用程序可以使用的主题,例如提供个性化布局。

    MultipartResolver:解析多部分请求,以支持从HTML表单上传文件。

    FlashMapManager:存储并检索可用于将一个请求属性传递到另一个请求的input和output的FlashMap,通常用于重定向。

    在Web MVC框架中,每个DispatcherServlet都拥自己的WebApplicationContext,它继承了ApplicationContext。WebApplicationContext包含了其上下文和Servlet实例之间共享的所有的基础框架beans。


    • HandlerMapping:


    HandlerMapping接口处理请求的映射

    HandlerMapping接口的实现类:

    SimpleUrlHandlerMapping类通过配置文件把URL映射到Controller类。

    DefaultAnnotationHandlerMapping类通过注解把URL映射到Controller类。


    • HandlerAdapter:


    HandlerAdapter接口-处理请求映射

    AnnotationMethodHandlerAdapter:通过注解,把请求URL映射到Controller类的方法上。

    • HandlerExceptionResolver


    HandlerExceptionResolver接口-异常处理接口

    SimpleMappingExceptionResolver通过配置文件进行异常处理。

    AnnotationMethodHandlerExceptionResolver:通过注解进行异常处理。

    • ViewResolver:

    ViewResolver接口解析View视图。

    UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理。


    参考资料:http://elf8848.iteye.com/blog/875830

    展开全文
  • c# mvc框架原理

    千次阅读 2019-09-06 11:09:09
    一、MVC原理解析 二、HttpHandler 1.HttpHandler,IHttpHandler,MvcHandler的说明 2.IHttpHandler解析 3.MvcHandler解析 三、HttpModule 1.HttpModule能干什么 2.HttpModule的使用 3....
  • MVC设计原理讲述

    2019-04-28 08:43:32
    首先说明,MVC设计模式可以带来更好的软件结构和代码重用。 MVC是如何工作的呢? MVC是一个设计模式,它强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分成三个核心部件:模型、视图、控制器。它们...
  • 深入理解MVC原理

    万次阅读 2017-02-17 15:47:18
    Web MVC简介 1.1、Web开发中的请求-响应模型:   在Web世界里,具体步骤如下: 1、 Web浏览器(如IE)发起请求,如访问http://sishuok.com 2、 Web服务器(如Tomcat)接收请求,处理请求(比如...
  • MVC原理

    2019-09-24 15:52:44
    SP.NET MVC由以下两个核心组成部分构成:  一个名为UrlRoutingModule的自定义HttpModule,用来解析Controller与Action名称;  一个名为MvcHandler的自定义HttpHandler,用来实现对Controller的激活和Action...
  • 【Spring】Spring MVC原理及配置详解

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

    2010-06-30 10:52:00
    什么是MVC模式MVC(Model-View-Controller,模型—视图—控制器模式)用于表示一种软件架构模式。它把软件系统分为三个基本部分:模型 (Model),视图(View)和控制器(Controller)。那么MVC模式和我们熟悉的...
  • SpringBoot——MVC原理

    千次阅读 2018-11-04 22:05:17
    【1】Spring MVC auto-configuration(可参考官方文档):Spring Boot 自动配置好了SpringMVC。以下是 SpringBoot 对 SpringMVC 的默认配置:(WebMvcAutoConfiguration) ● Inclusion of ...
  • spring MVC 原理

    2017-11-27 14:52:17
    springmvc架构流程图: ...第一步:用户发送请求到前端控制器(DispatcherServlet) ...第二步:前端控制器请求HandlerMapping查找 Handler 【可以根据xml配置、注解进行查找】 ...第三步:处理器映射器HandlerMappin
  • 仅此一文让你明白ASP.NET MVC原理

    千次阅读 2017-09-25 14:06:49
    ASP.NET MVC由以下两个核心组成部分构成:一个名为UrlRoutingModule的自定义HttpModule,用来解析Controller与Action名称;一个名为MvcHandler的自定义HttpHandler,用来实现对Controller的激活和Action的执行;!!...
  • spring MVC原理

    万次阅读 多人点赞 2013-04-13 11:54:48
    Spring MVC工作流程图 图一 图二  Spring工作流程描述  1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;  2. DispatcherServlet对请求URL进行解析,...
  • Spring mvc 原理浅析

    千次阅读 2016-08-17 10:57:55
    Spring MVC主要包括以下要点: 1:由DispatcherServlet控制的整个流程; 2:注解驱动的控制器,其中包括请求映射、数据的绑定和格式化; 3:文件上传; 4:一些杂项,如静态资源处理、异常处理等等。 ...
  • mvc原理和mvc模式的优缺点

    千次阅读 2018-09-20 21:20:20
    mvc原理和mvc模式的优缺点 一、mvc原理   mvc是一种程序开发设计模式,它实现了显示模块与功能模块的分离。提高了程序的可维护性、可移植性、可扩展性与可重用性,降低了程序的开发难度。它主要分模型、视图、控制...
  • 前言:spring mvc 是当前最为流行的一种java WEB 框架。在还没有spring boot以前,通常搭配tomcat等容器进行web项目的开发。而现在spring全家桶越来越完善。慢慢脱离来用容器来启动web项目。那么spring boot 搭配...
  • 进入web.xml中配置的Spring拦截(调度)器:   调用堆栈信息: 开始拦截,这一步主要是根据request获取handler(该handler可以配置): ...(接上面方法)这段代码中的if(isGet…主要解决浏览器的缓存问题,如果...
  • Spring IOC DI MVC原理

    2019-08-31 00:01:06
  • struts,struts2,spring MVC原理及区别

    千次阅读 多人点赞 2020-03-12 18:41:31
    struts,struts2,spring MVCMVC思想Struts原理 MVC思想 在Web开发中,MVC思想如下: M(Model),封装应用程序的数据结构和事务逻辑,集中体现应用程序的状态,当数据状态发生改变的时候,能够在视图中显示出来,...
1 2 3 4 5 ... 20
收藏数 98,708
精华内容 39,483
关键字:

mvc原理