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本質論》

 

如有說的不清楚或者錯誤的地方,還望指正