本文将介绍Spring.Net(不仅仅是Spring.Net,其实所有的IoC容器要向控制器中进行注入,原理都是差不多的)在MVC控制器中依赖注入的实现原理,本文并没有关于在MVC使用Spring怎么配置,怎么使用,怎么实现。 引言放在前面,只是为了避免浪费你的时间。 望你能静心片刻,认真阅读。 防止爬虫,加个链接:https://www.cnblogs.com/MedlarCanFly/p/11488689.html 情景 View Code 每次看代码都有不一样的理解,今天我在看MVC控制器中一个通过Spring.Net依赖注入的UserInfoService属性时,突然有些疑问,注入的前提是控制反转,这么说我的Controller是从IoC容器中来的了?但是我不记得在哪个地方有配置额,对此我展开了深入的研究。 从MVC本身开始 首先我们要搞懂MVC本身是通过什么方式获取控制器对象的,本质如果都没有搞懂,又何来扩展呢? 在MVC模式下,通过实现IControllerFactory接口的对象来获取当前请求的控制器对象,实现IControllerFactory接口的对象也就是控制器的创建工厂。 简单看下IControllerFactory 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 <>9__20_0; 39 public static EndInvokeVoidDelegate <>9__20_1; 40 public static Func, bool> <>9__26_0; 41 42 // Methods 43 static <>c(); 44 public <>c(); 45 internal IAsyncResult b__20_0(AsyncCallback asyncCallback, object asyncState, MvcHandler.ProcessRequestState innerState); 46 internal void b__20_1(IAsyncResult asyncResult, MvcHandler.ProcessRequestState innerState); 47 internal bool b__26_0(KeyValuePair 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接口的。 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的毛都没看到..... 其实,如果你是认真读过来的,答案在你心中应该已经有了。 打开折叠,就是答案 View Code 关于代码我想就不用过多解释了,有了上面的知识基础,这就是一看就懂的那种。 算了,我还是说一下CreateController方法吧,防止有不熟悉Spring.Net的小伙伴。 ApplicationContext:这就是相当于IoC容器的东西 ApplicationContext.ContainsObjectDefinition(controllerName):返回容器中是否存在名称为controllerName的对象 总结 仔细品味每一行代码,会发现任何东西都没有表面上那么简单,每一个实现的背后都值得深入研究。 码了这么长时间,希望能对正在阅读的你有所帮助。https://www.cnblogs.com/MedlarCanFly/p/11488689.html