本篇文章為大家展示了使用Spring.Net怎么在MVC中實現注入,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
情景
public class HomeController : Controller { //這是一個很神奇的注入 private IBLL.IUserInfoService UserInfoService { get; set; } public ActionResult Index() { return Content(UserInfoService.GetName()); } }
每次看代碼都有不一樣的理解,今天我在看MVC控制器中一個通過Spring.Net依賴注入的UserInfoService屬性時,突然有些疑問,注入的前提是控制反轉,這么說我的Controller是從IoC容器中來的了?但是我不記得在哪個地方有配置額,對此我展開了深入的研究。
從MVC本身開始
首先我們要搞懂MVC本身是通過什么方式獲取控制器對象的,本質如果都沒有搞懂,又何來擴展呢?
在MVC模式下,通過實現IControllerFactory接口的對象來獲取當前請求的控制器對象,實現IControllerFactory接口的對象也就是控制器的創建工廠。
簡單看下IControllerFactory
// // 摘要: // 定義控制器工廠所需的方法。 public interface IControllerFactory { // // 摘要: // 使用指定的請求上下文來創建指定的控制器。 // // 參數: // requestContext: // 請求上下文。 // // controllerName: // 控制器的名稱。 // // 返回結果: // 控制器。 IController CreateController(RequestContext requestContext, string controllerName); // // 摘要: // 獲取控制器的會話行為。 // // 參數: // requestContext: // 請求上下文。 // // controllerName: // 你想要獲取器其會話行為的控制器的名稱。 // // 返回結果: // 控制器的會話行為。 SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName); // // 摘要: // 釋放指定的控制器。 // // 參數: // controller: // 控制器。 void ReleaseController(IController controller); }
一個Http請求過來,選擇哪個控制器是通過MvcHandler來處理的
控制器工廠是通過ControllerBuilder的Current屬性提供給MvcHandler使用的
下面的代碼是反編譯過來的,簡單看下即可(因為我要標記黃色高亮部分,所以沒有折疊)
internal ControllerBuilder ControllerBuilder { get { if (this._controllerBuilder == null) { this._controllerBuilder = ControllerBuilder.Current; } return this._controllerBuilder; } set { this._controllerBuilder = value; } }
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState { // Fields private ControllerBuilder _controllerBuilder; private static readonly object _processRequestTag; internal static readonly string MvcVersion; public static readonly string MvcVersionHeaderName; // Methods 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); // Properties internal ControllerBuilder ControllerBuilder { get; set; } public static bool DisableMvcResponseHeader { get; [CompilerGenerated] set; } protected virtual bool IsReusable { get; } public RequestContext RequestContext { get; [CompilerGenerated] private set; } bool IHttpHandler.IsReusable { get; } // Nested Types [Serializable, CompilerGenerated] private sealed class <>c { // Fields public static readonly MvcHandler.<>c <>9; public static BeginInvokeDelegate<MvcHandler.ProcessRequestState> <>9__20_0; public static EndInvokeVoidDelegate<MvcHandler.ProcessRequestState> <>9__20_1; public static Func<KeyValuePair<string, object>, bool> <>9__26_0; // Methods static <>c(); public <>c(); internal IAsyncResult <BeginProcessRequest>b__20_0(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState); internal void <BeginProcessRequest>b__20_1(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState); internal bool <RemoveOptionalRoutingParameters>b__26_0(KeyValuePair<string, object> entry); } [StructLayout(LayoutKind.Sequential)] private struct ProcessRequestState { internal IAsyncController AsyncController; internal IControllerFactory Factory; internal RequestContext RequestContext; internal void ReleaseController(); } }
默認工廠
默認情況下,在ControllerBuilder內部會創建一個DefaultControllerFactory類型的對象,以提供處理請求。
DefaultControllerFactory是實現IControllerFactory接口的。
// // 摘要: // 表示默認情況下已注冊的控制器工廠。 public class DefaultControllerFactory : IControllerFactory { // // 摘要: // 初始化 System.Web.Mvc.DefaultControllerFactory 類的新實例。 public DefaultControllerFactory(); // // 摘要: // 使用控制器激活器來初始化 System.Web.Mvc.DefaultControllerFactory 類的新實例。 // // 參數: // controllerActivator: // 實現控制器激活器接口的對象。 public DefaultControllerFactory(IControllerActivator controllerActivator); // // 摘要: // 使用指定的請求上下文來創建指定的控制器。 // // 參數: // requestContext: // HTTP 請求的上下文,其中包括 HTTP 上下文和路由數據。 // // controllerName: // 控制器的名稱。 // // 返回結果: // 控制器。 // // 異常: // T:System.ArgumentNullException: // requestContext 參數為 null。 // // T:System.ArgumentException: // controllerName 參數為 null 或為空。 public virtual IController CreateController(RequestContext requestContext, string controllerName); // // 摘要: // 釋放指定的控制器。 // // 參數: // controller: // 要釋放的控制器。 public virtual void ReleaseController(IController controller); // // 摘要: // 檢索指定請求上下文和控制器類型的控制器實例。 // // 參數: // requestContext: // HTTP 請求的上下文,其中包括 HTTP 上下文和路由數據。 // // controllerType: // 控制器的類型。 // // 返回結果: // 控制器實例。 // // 異常: // T:System.Web.HttpException: // controllerType 為 null。 // // T:System.ArgumentException: // 無法分配 controllerType。 // // T:System.InvalidOperationException: // 無法創建 controllerType 的實例。 protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType); // // 摘要: // 返回控制器的會話行為。 // // 參數: // requestContext: // 請求上下文。 // // controllerType: // 控制器的類型。 // // 返回結果: // 控制器的會話行為。 protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType); // // 摘要: // 檢索指定名稱和請求上下文的控制器類型。 // // 參數: // requestContext: // HTTP 請求的上下文,其中包括 HTTP 上下文和路由數據。 // // controllerName: // 控制器的名稱。 // // 返回結果: // 控制器類型。 protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName); }
默認情況下,Controller類需要提供默認的構造函數,因為DefaultControllerFactory是通過反射來創建Controller對象實例的。
如果我們定義的Controller需要通過構造函數創建,或者通過某個IoC容器管理Controller,可以通過自定義控制器工廠來實現。
自定義控制器工廠
為什么說這么多關于控制器工廠的東西呢,其實Spring.Net就是通過繼承DefaultControllerFactory創建SpringControllerFactory的。
說了這么多就是為了后面可以更容易的理解Spring.Net的控制器工廠源碼罷了。
回歸正題,接著創建自己的控制器工廠。
1.Home控制器內容如下
public class HomeController : Controller { private IUserInfoService UserInfoService { get; set; } public HomeController(IUserInfoService userInfoService) { UserInfoService = userInfoService; } public ActionResult Index() { return Content(UserInfoService.GetName()); } }
這里的UserInfoService只是一個很簡陋的測試類,只有一個GetName()方法用來返回“小明”。
接下來將通過自定義控制器工廠實現構造注入UserInfoService
2.創建控制器工廠MyControllerFactory
為了方便我直接繼承了DefaultControllerFactory,當然也可以通過實現IControllerFactory來創建
public class MyControllerFactory : DefaultControllerFactory { private static readonly IBLL.IUserInfoService userInfoService = new BLL.UserInfoService(); //重寫CreateController public override IController CreateController(RequestContext requestContext, string controllerName) { IController controller = null; if (controllerName == "Home") { //如果是我們制定的Home控制器則給其實例化,并通過構造參數注入userInfoService controller = new HomeController(userInfoService); } else { //通過默認控制器工廠創建控制器 controller = base.CreateController(requestContext, controllerName); } return controller; } }
3.在Global.asax中注冊
protected void Application_Start() { MyControllerFactory myControllerFactory = new MyControllerFactory(); //通過ControllerBuilder設置制定的控制器工廠 ControllerBuilder.Current.SetControllerFactory(myControllerFactory); AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); }
4.運行測試(神奇不再神奇)
意料之外,情理之中,我們并沒有在控制器中實例化,結果卻出來了
(實例化在工廠中完成了)
Spring.Net注入原理
說了這么多,回頭看看標題“Spring.Net是怎么在MVC中實現注入的”,你倒是說啊,等的花都謝了,連Spring.Net的毛都沒看到.....
其實,如果你是認真讀過來的,答案在你心中應該已經有了。
答案如下
namespace Spring.Web.Mvc { /// <summary> /// Controller Factory for ASP.NET MVC /// </summary> public class SpringControllerFactory : DefaultControllerFactory { private static IApplicationContext _context; /// <summary> /// Gets the application context. /// </summary> /// <value>The application context.</value> public static IApplicationContext ApplicationContext { get { if (_context == null || _context.Name != ApplicationContextName) { if (string.IsNullOrEmpty(ApplicationContextName)) { _context = ContextRegistry.GetContext(); } else { _context = ContextRegistry.GetContext(ApplicationContextName); } } return _context; } } /// <summary> /// Gets or sets the name of the application context. /// </summary> /// <remarks> /// Defaults to using the root (default) Application Context. /// </remarks> /// <value>The name of the application context.</value> public static string ApplicationContextName { get; set; } /// <summary> /// Creates the specified controller by using the specified request context. /// </summary> /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param> /// <param name="controllerName">The name of the controller.</param> /// <returns>A reference to the controller.</returns> /// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext"/> parameter is null.</exception> /// <exception cref="T:System.ArgumentException">The <paramref name="controllerName"/> parameter is null or empty.</exception> public override IController CreateController(RequestContext requestContext, string controllerName) { IController controller; if (ApplicationContext.ContainsObjectDefinition(controllerName)) { controller = ApplicationContext.GetObject(controllerName) as IController; } else { controller = base.CreateController(requestContext, controllerName); } AddActionInvokerTo(controller); return controller; } /// <summary> /// Retrieves the controller instance for the specified request context and controller type. /// </summary> /// <param name="requestContext">The context of the HTTP request, which includes the HTTP context and route data.</param> /// <param name="controllerType">The type of the controller.</param> /// <returns>The controller instance.</returns> /// <exception cref="T:System.Web.HttpException"> /// <paramref name="controllerType"/> is null.</exception> /// <exception cref="T:System.ArgumentException"> /// <paramref name="controllerType"/> cannot be assigned.</exception> /// <exception cref="T:System.InvalidOperationException">An instance of <paramref name="controllerType"/> cannot be created.</exception> protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { IController controller = null; if (controllerType != null) { var controllers = ApplicationContext.GetObjectsOfType(controllerType); if (controllers.Count > 0) { controller = (IController)controllers.First().Value; } } if (controller == null) { //pass to base class for remainder of handling if can't find it in the context controller = base.GetControllerInstance(requestContext, controllerType); } AddActionInvokerTo(controller); return controller; } /// <summary> /// Adds the action invoker to the controller instance. /// </summary> /// <param name="controller">The controller.</param> protected virtual void AddActionInvokerTo(IController controller) { if (controller == null) return; if (typeof(Controller).IsAssignableFrom(controller.GetType())) { ((Controller)controller).ActionInvoker = new SpringActionInvoker(ApplicationContext); } } } }
上述內容就是使用Spring.Net怎么在MVC中實現注入,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。