首页 > 编程 > Java > 正文

Spring.Net在MVC中实现注入的原理解析

2019-11-26 08:36:24
字体:
来源:转载
供稿:网友

前言

本文将介绍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的对象

总结

仔细品味每一行代码,会发现任何东西都没有表面上那么简单,每一个实现的背后都值得深入研究。

码了这么长时间,希望能对正在阅读的你有所帮助。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表