zoukankan      html  css  js  c++  java
  • Spring.Net是怎么在MVC中实现注入的(原理)

    本文将介绍Spring.Net(不仅仅是Spring.Net,其实所有的IoC容器要向控制器中进行注入,原理都是差不多的)在MVC控制器中依赖注入的实现原理,本文并没有关于在MVC使用Spring怎么配置,怎么使用,怎么实现。

    引言放在前面,只是为了避免浪费你的时间。

    望你能静心片刻,认真阅读。

    防止爬虫,加个链接:https://www.cnblogs.com/MedlarCanFly/p/11488689.html

    情景

    1     public class HomeController : Controller
    2     {
    3         //这是一个很神奇的注入
    4         private IBLL.IUserInfoService UserInfoService { get; set; }
    5         public ActionResult Index()
    6         {
    7             return Content(UserInfoService.GetName());
    8         }
    9     }
    View Code

    每次看代码都有不一样的理解,今天我在看MVC控制器中一个通过Spring.Net依赖注入的UserInfoService属性时,突然有些疑问,注入的前提是控制反转,这么说我的Controller是从IoC容器中来的了?但是我不记得在哪个地方有配置额,对此我展开了深入的研究。

    从MVC本身开始

    首先我们要搞懂MVC本身是通过什么方式获取控制器对象的,本质如果都没有搞懂,又何来扩展呢?

    在MVC模式下,通过实现IControllerFactory接口的对象来获取当前请求的控制器对象,实现IControllerFactory接口的对象也就是控制器的创建工厂。

    简单看下IControllerFactory

     1     //
     2     // 摘要:
     3     //     定义控制器工厂所需的方法。
     4     public interface IControllerFactory
     5     {
     6         //
     7         // 摘要:
     8         //     使用指定的请求上下文来创建指定的控制器。
     9         //
    10         // 参数:
    11         //   requestContext:
    12         //     请求上下文。
    13         //
    14         //   controllerName:
    15         //     控制器的名称。
    16         //
    17         // 返回结果:
    18         //     控制器。
    19         IController CreateController(RequestContext requestContext, string controllerName);
    20         //
    21         // 摘要:
    22         //     获取控制器的会话行为。
    23         //
    24         // 参数:
    25         //   requestContext:
    26         //     请求上下文。
    27         //
    28         //   controllerName:
    29         //     你想要获取器其会话行为的控制器的名称。
    30         //
    31         // 返回结果:
    32         //     控制器的会话行为。
    33         SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
    34         //
    35         // 摘要:
    36         //     释放指定的控制器。
    37         //
    38         // 参数:
    39         //   controller:
    40         //     控制器。
    41         void ReleaseController(IController controller);
    42     }
    View Code

    一个Http请求过来,选择哪个控制器是通过MvcHandler来处理的

    控制器工厂是通过ControllerBuilder的Current属性提供给MvcHandler使用的

    下面的代码是反编译过来的,简单看下即可(因为我要标记黄色高亮部分,所以没有折叠)

     1 internal ControllerBuilder ControllerBuilder
     2 {
     3     get
     4     {
     5         if (this._controllerBuilder == null)
     6         {
     7             this._controllerBuilder = ControllerBuilder.Current;
     8         }
     9         return this._controllerBuilder;
    10     }
    11     set
    12     {
    13         this._controllerBuilder = value;
    14     }
    15 }
     1 public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
     2 {
     3     // Fields
     4     private ControllerBuilder _controllerBuilder;
     5     private static readonly object _processRequestTag;
     6     internal static readonly string MvcVersion;
     7     public static readonly string MvcVersionHeaderName;
     8 
     9     // Methods
    10     static MvcHandler();
    11     public MvcHandler(RequestContext requestContext);
    12     protected internal virtual void AddVersionHeader(HttpContextBase httpContext);
    13     protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state);
    14     protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state);
    15     protected internal virtual void EndProcessRequest(IAsyncResult asyncResult);
    16     private static string GetMvcVersionString();
    17     protected virtual void ProcessRequest(HttpContext httpContext);
    18     protected internal virtual void ProcessRequest(HttpContextBase httpContext);
    19     private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory);
    20     private void RemoveOptionalRoutingParameters();
    21     IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
    22     void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result);
    23     void IHttpHandler.ProcessRequest(HttpContext httpContext);
    24 
    25     // Properties
    26     internal ControllerBuilder ControllerBuilder { get; set; }
    27     public static bool DisableMvcResponseHeader { get; [CompilerGenerated] set; }
    28     protected virtual bool IsReusable { get; }
    29     public RequestContext RequestContext { get; [CompilerGenerated] private set; }
    30     bool IHttpHandler.IsReusable { get; }
    31 
    32     // Nested Types
    33     [Serializable, CompilerGenerated]
    34     private sealed class <>c
    35     {
    36         // Fields
    37         public static readonly MvcHandler.<>c <>9;
    38         public static BeginInvokeDelegate<MvcHandler.ProcessRequestState> <>9__20_0;
    39         public static EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> <>9__20_1;
    40         public static Func<KeyValuePair<string, object>, bool> <>9__26_0;
    41 
    42         // Methods
    43         static <>c();
    44         public <>c();
    45         internal IAsyncResult <BeginProcessRequest>b__20_0(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState);
    46         internal void <BeginProcessRequest>b__20_1(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState);
    47         internal bool <RemoveOptionalRoutingParameters>b__26_0(KeyValuePair<string, object> entry);
    48     }
    49 
    50     [StructLayout(LayoutKind.Sequential)]
    51     private struct ProcessRequestState
    52     {
    53         internal IAsyncController AsyncController;
    54         internal IControllerFactory Factory;
    55         internal RequestContext RequestContext;
    56         internal void ReleaseController();
    57     }
    58 }

    默认工厂

    默认情况下,在ControllerBuilder内部会创建一个DefaultControllerFactory类型的对象,以提供处理请求。

    DefaultControllerFactory是实现IControllerFactory接口的。

      1     //
      2     // 摘要:
      3     //     表示默认情况下已注册的控制器工厂。
      4     public class DefaultControllerFactory : IControllerFactory
      5     {
      6         //
      7         // 摘要:
      8         //     初始化 System.Web.Mvc.DefaultControllerFactory 类的新实例。
      9         public DefaultControllerFactory();
     10         //
     11         // 摘要:
     12         //     使用控制器激活器来初始化 System.Web.Mvc.DefaultControllerFactory 类的新实例。
     13         //
     14         // 参数:
     15         //   controllerActivator:
     16         //     实现控制器激活器接口的对象。
     17         public DefaultControllerFactory(IControllerActivator controllerActivator);
     18 
     19         //
     20         // 摘要:
     21         //     使用指定的请求上下文来创建指定的控制器。
     22         //
     23         // 参数:
     24         //   requestContext:
     25         //     HTTP 请求的上下文,其中包括 HTTP 上下文和路由数据。
     26         //
     27         //   controllerName:
     28         //     控制器的名称。
     29         //
     30         // 返回结果:
     31         //     控制器。
     32         //
     33         // 异常:
     34         //   T:System.ArgumentNullException:
     35         //     requestContext 参数为 null。
     36         //
     37         //   T:System.ArgumentException:
     38         //     controllerName 参数为 null 或为空。
     39         public virtual IController CreateController(RequestContext requestContext, string controllerName);
     40         //
     41         // 摘要:
     42         //     释放指定的控制器。
     43         //
     44         // 参数:
     45         //   controller:
     46         //     要释放的控制器。
     47         public virtual void ReleaseController(IController controller);
     48         //
     49         // 摘要:
     50         //     检索指定请求上下文和控制器类型的控制器实例。
     51         //
     52         // 参数:
     53         //   requestContext:
     54         //     HTTP 请求的上下文,其中包括 HTTP 上下文和路由数据。
     55         //
     56         //   controllerType:
     57         //     控制器的类型。
     58         //
     59         // 返回结果:
     60         //     控制器实例。
     61         //
     62         // 异常:
     63         //   T:System.Web.HttpException:
     64         //     controllerType 为 null。
     65         //
     66         //   T:System.ArgumentException:
     67         //     无法分配 controllerType。
     68         //
     69         //   T:System.InvalidOperationException:
     70         //     无法创建 controllerType 的实例。
     71         protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType);
     72         //
     73         // 摘要:
     74         //     返回控制器的会话行为。
     75         //
     76         // 参数:
     77         //   requestContext:
     78         //     请求上下文。
     79         //
     80         //   controllerType:
     81         //     控制器的类型。
     82         //
     83         // 返回结果:
     84         //     控制器的会话行为。
     85         protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType);
     86         //
     87         // 摘要:
     88         //     检索指定名称和请求上下文的控制器类型。
     89         //
     90         // 参数:
     91         //   requestContext:
     92         //     HTTP 请求的上下文,其中包括 HTTP 上下文和路由数据。
     93         //
     94         //   controllerName:
     95         //     控制器的名称。
     96         //
     97         // 返回结果:
     98         //     控制器类型。
     99         protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName);
    100     }
    View Code

    默认情况下,Controller类需要提供默认的构造函数,因为DefaultControllerFactory是通过反射来创建Controller对象实例的。

    如果我们定义的Controller需要通过构造函数创建,或者通过某个IoC容器管理Controller,可以通过自定义控制器工厂来实现。

    自定义控制器工厂

    为什么说这么多关于控制器工厂的东西呢,其实Spring.Net就是通过继承DefaultControllerFactory创建SpringControllerFactory的。

    说了这么多就是为了后面可以更容易的理解Spring.Net的控制器工厂源码罢了。

    回归正题,接着创建自己的控制器工厂。

    1.Home控制器内容如下

     1     public class HomeController : Controller
     2     {
     3         private IUserInfoService UserInfoService { get; set; }
     4         public HomeController(IUserInfoService userInfoService)
     5         {
     6             UserInfoService = userInfoService;
     7         }
     8         public ActionResult Index()
     9         {
    10             return Content(UserInfoService.GetName());
    11         }
    12     }

    这里的UserInfoService只是一个很简陋的测试类,只有一个GetName()方法用来返回“小明”。

    接下来将通过自定义控制器工厂实现构造注入UserInfoService

    2.创建控制器工厂MyControllerFactory

    为了方便我直接继承了DefaultControllerFactory,当然也可以通过实现IControllerFactory来创建

     1     public class MyControllerFactory : DefaultControllerFactory
     2     {
     3         private static readonly IBLL.IUserInfoService userInfoService = new BLL.UserInfoService();
     4 
     5         //重写CreateController
     6         public override IController CreateController(RequestContext requestContext, string controllerName)
     7         {
     8             IController controller = null;
     9             if (controllerName == "Home")
    10             {
    11                 //如果是我们制定的Home控制器则给其实例化,并通过构造参数注入userInfoService
    12                 controller = new HomeController(userInfoService);
    13             }
    14             else
    15             {
    16                 //通过默认控制器工厂创建控制器
    17                 controller = base.CreateController(requestContext, controllerName);
    18             }
    19             return controller;
    20         }
    21     }

    3.在Global.asax中注册

    1         protected void Application_Start()
    2         {
    3             MyControllerFactory myControllerFactory = new MyControllerFactory();
    4             //通过ControllerBuilder设置制定的控制器工厂
    5             ControllerBuilder.Current.SetControllerFactory(myControllerFactory);
    6             AreaRegistration.RegisterAllAreas();
    7             RouteConfig.RegisterRoutes(RouteTable.Routes);
    8         }

    4.运行测试(神奇不再神奇)

    意料之外,情理之中,我们并没有在控制器中实例化,结果却出来了

    (实例化在工厂中完成了)

    Spring.Net注入原理

    说了这么多,回头看看标题“Spring.Net是怎么在MVC中实现注入的”,你倒是说啊,等的花都谢了,连Spring.Net的毛都没看到.....

    其实,如果你是认真读过来的,答案在你心中应该已经有了。

    打开折叠,就是答案

      1 namespace Spring.Web.Mvc
      2 {
      3     /// <summary>
      4     /// Controller Factory for ASP.NET MVC
      5     /// </summary>
      6     public class SpringControllerFactory : DefaultControllerFactory
      7     {
      8         private static IApplicationContext _context;
      9 
     10         /// <summary>
     11         /// Gets the application context.
     12         /// </summary>
     13         /// <value>The application context.</value>
     14         public static IApplicationContext ApplicationContext
     15         {
     16             get
     17             {
     18                 if (_context == null || _context.Name != ApplicationContextName)
     19                 {
     20                     if (string.IsNullOrEmpty(ApplicationContextName))
     21                     {
     22                         _context = ContextRegistry.GetContext();
     23                     }
     24                     else
     25                     {
     26                         _context = ContextRegistry.GetContext(ApplicationContextName);
     27                     }
     28                 }
     29 
     30                 return _context;
     31             }
     32         }
     33 
     34         /// <summary>
     35         /// Gets or sets the name of the application context.
     36         /// </summary>
     37         /// <remarks>
     38         /// Defaults to using the root (default) Application Context.
     39         /// </remarks>
     40         /// <value>The name of the application context.</value>
     41         public static string ApplicationContextName { get; set; }
     42 
     43         /// <summary>
     44         /// Creates the specified controller by using the specified request context.
     45         /// </summary>
     46         /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
     47         /// <param name="controllerName">The name of the controller.</param>
     48         /// <returns>A reference to the controller.</returns>
     49         /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext"/> parameter is null.</exception>
     50         /// <exception cref="T:System.ArgumentException">The <paramref name="controllerName"/> parameter is null or empty.</exception>
     51         public override IController CreateController(RequestContext requestContext, string controllerName)
     52         {
     53             IController controller;
     54 
     55             if (ApplicationContext.ContainsObjectDefinition(controllerName))
     56             {
     57                 controller = ApplicationContext.GetObject(controllerName) as IController;
     58             }
     59             else
     60             {
     61                 controller = base.CreateController(requestContext, controllerName);
     62             }
     63 
     64             AddActionInvokerTo(controller);
     65 
     66             return controller;
     67         }
     68 
     69         /// <summary>
     70         /// Retrieves the controller instance for the specified request context and controller type.
     71         /// </summary>
     72         /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param>
     73         /// <param name="controllerType">The type of the controller.</param>
     74         /// <returns>The controller instance.</returns>
     75         /// <exception cref="T:System.Web.HttpException">
     76         ///     <paramref name="controllerType"/> is null.</exception>
     77         /// <exception cref="T:System.ArgumentException">
     78         ///     <paramref name="controllerType"/> cannot be assigned.</exception>
     79         /// <exception cref="T:System.InvalidOperationException">An instance of <paramref name="controllerType"/> cannot be created.</exception>
     80         protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
     81         {
     82             IController controller = null;
     83 
     84             if (controllerType != null)
     85             {
     86                 var controllers = ApplicationContext.GetObjectsOfType(controllerType);
     87                 if (controllers.Count > 0)
     88                 {
     89                     controller = (IController)controllers.First().Value;
     90                 }
     91             }
     92 
     93             if (controller == null)
     94             {
     95                 //pass to base class for remainder of handling if can't find it in the context
     96                 controller = base.GetControllerInstance(requestContext, controllerType);
     97             }
     98             
     99             AddActionInvokerTo(controller);
    100 
    101             return controller;
    102         }
    103 
    104         /// <summary>
    105         /// Adds the action invoker to the controller instance.
    106         /// </summary>
    107         /// <param name="controller">The controller.</param>
    108         protected virtual void AddActionInvokerTo(IController controller)
    109         {
    110             if (controller == null)
    111                 return;
    112 
    113             if (typeof(Controller).IsAssignableFrom(controller.GetType()))
    114             {
    115                 ((Controller)controller).ActionInvoker = new SpringActionInvoker(ApplicationContext);
    116             }
    117         }
    118 
    119     }
    120 }
    View Code

    关于代码我想就不用过多解释了,有了上面的知识基础,这就是一看就懂的那种。

    算了,我还是说一下CreateController方法吧,防止有不熟悉Spring.Net的小伙伴。

    ApplicationContext:这就是相当于IoC容器的东西

    ApplicationContext.ContainsObjectDefinition(controllerName):返回容器中是否存在名称为controllerName的对象

    总结

    仔细品味每一行代码,会发现任何东西都没有表面上那么简单,每一个实现的背后都值得深入研究。

    码了这么长时间,希望能对正在阅读的你有所帮助。

    参考书籍:《ASP.NET本质论》

    如有说的不清楚或者错误的地方,还望指正

  • 相关阅读:
    web架构
    网站开发的学习交流 系统架构 负载均衡
    数据库连接
    OpenCV4【12】边缘检测
    python基础_格式化输出(%用法和format用法)
    Python之telnetlib模块
    根据文字或图片来生成用于Banner输出的字符画
    Python3 range() 函数用法
    Python psutil cpu_percent调用说明
    @staticmethod和@classmethod的用法
  • 原文地址:https://www.cnblogs.com/MedlarCanFly/p/11488689.html
Copyright © 2011-2022 走看看