Spring.Net是怎么在MVC中实现注入的(原理)

  • 2019 年 10 月 3 日
  • 笔记

本文将介绍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本质论》

 

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