本文将介绍 Spring.NET(不仅仅是 Spring.NET, 其实所有的 IoC 容器要向控制器中进行注入, 原理都是差不多的) 在 MVC 控制器中依赖注入的实现原理, 本文并没有关于在 MVC 使用 Spring 怎么配置, 怎么使用, 怎么实现.
引言放在前面, 只是为了避免浪费你的时间.
望你能静心片刻, 认真阅读.
防止爬虫, 加个链接: https://www.cnblogs.com/MedlarCanFly/p/11488689.html
情景
- public class HomeController : Controller
- {
- // 这是一个很神奇的注入
- private IBLL.IUserInfoService UserInfoService { get; set; }
- public ActionResult Index()
- {
- return Content(UserInfoService.GetName());
- }
- }
- View Code
每次看代码都有不一样的理解, 今天我在看 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);
- }
- View Code
一个 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);
- }
- View Code
默认情况下, 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);
- }
- }
- }
- }
- View Code
关于代码我想就不用过多解释了, 有了上面的知识基础, 这就是一看就懂的那种.
算了, 我还是说一下 CreateController 方法吧, 防止有不熟悉 Spring.NET 的小伙伴.
ApplicationContext: 这就是相当于 IoC 容器的东西
ApplicationContext.ContainsObjectDefinition(controllerName): 返回容器中是否存在名称为 controllerName 的对象
总结
仔细品味每一行代码, 会发现任何东西都没有表面上那么简单, 每一个实现的背后都值得深入研究.
码了这么长时间, 希望能对正在阅读的你有所帮助.
参考书籍:《ASP.NET 本质论》
如有说的不清楚或者错误的地方, 还望指正
来源: https://www.cnblogs.com/MedlarCanFly/p/11488689.html