Spring.Net在MVC中实现注入的原理解析
作者:MedlarCanFly
前言
本文将介绍Spring.Net(不仅仅是Spring.Net,其实所有的IoC容器要向控制器中进行注入,原理都是差不多的)在MVC控制器中依赖注入的实现原理,本文并没有关于在MVC使用Spring怎么配置,怎么使用,怎么实现。
引言放在前面,只是为了避免浪费你的时间。
望你能静心片刻,认真阅读。
情景
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); } } } }
关于代码我想就不用过多解释了,有了上面的知识基础,这就是一看就懂的那种。
算了,我还是说一下CreateController方法吧,防止有不熟悉Spring.Net的小伙伴。
ApplicationContext:这就是相当于IoC容器的东西
ApplicationContext.ContainsObjectDefinition(controllerName):返回容器中是否存在名称为controllerName的对象
总结
仔细品味每一行代码,会发现任何东西都没有表面上那么简单,每一个实现的背后都值得深入研究。
码了这么长时间,希望能对正在阅读的你有所帮助。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。