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 }
每次看程式碼都有不一樣的理解,今天我在看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 }
一個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本質論》
如有說的不清楚或者錯誤的地方,還望指正
&n