本篇文章为大家展示了使用Spring.Net怎么在MVC中实现注入,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
情景
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);
}
}
}
}
上述内容就是使用Spring.Net怎么在MVC中实现注入,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。