Spring.Net是怎麼在MVC中實現註入的(原理)

来源:https://www.cnblogs.com/MedlarCanFly/archive/2019/09/10/11488689.html
-Advertisement-
Play Games

本文將介紹Spring.Net(不僅僅是Spring.Net,其實所有的IoC容器要向控制器中進行註入,原理都是差不多的)在MVC控制器中依賴註入的實現原理,本文並沒有關於在MVC使用Spring怎麼配置,怎麼使用,怎麼實現。 引言放在前面,只是為了避免浪費你的時間。 望你能靜心片刻,認真閱讀。 防 ...


本文將介紹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 

              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 依賴註入主要是一種結構性的模式,註重的是類與類之間的結構,它要達到的目的就是設計原則中最少知道和合成復用的原則,減少內部依賴,履行單一職責,最終就是強解耦。依賴註入目前最好的實現就是依賴註入容器。 Unity是微軟Patterns & Practices團隊所開發的一個輕量級的,並且可擴展的依賴註入 ...
  • 場景 DevExpress的PanelControl常用進行窗體頁面的佈局。 一般是拖拽一個PannelControl,然後是再拖拽其他控制項。 如果是由代碼生成控制項並控制佈局的話,怎樣實現。 關註公眾號 霸道的程式猿 獲取編程相關電子書、教程推送與免費下載。 實現 比如說要在PanelContrl中 ...
  • 通過 abp(net core)+easyui+efcore實現倉儲管理系統——菜單-上 (十六)這篇文章,我們已經瞭解了ABP中的菜單相關的類及類的屬性與方法,接下我們通過實例來實現一個動態載入菜單的功能。動態菜單是我們在abp(net core)+easyui+efcore實現倉儲管理系統——領... ...
  • 場景 使用DevExpress的EditText控制項時,需要限制其輸入類型為數字。 正常來說是窗體上拖拽一個TextEdit,然後在設計視窗點擊小三角,選擇Change Mask 但是如果說TextEdit控制項不是拖拽上去而是由代碼生成的,那麼在代碼中怎樣設置只能輸入數字。 關註公眾號 霸道的程式猿 ...
  • 前提 入行已經7,8年了,一直想做一套漂亮點的自定義控制項,於是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_contr ...
  • Win10 IIS預設是.net 4.0,安裝VS2015後,IIS沒有.net 4.5,解決方法,直接在CMD命令行下執行下麵語句 ...
  • Asp.NetCore實現Aop,AspectCore實現Aop ...
  • 前提 入行已經7,8年了,一直想做一套漂亮點的自定義控制項,於是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_contr ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...