小程序容器助力企业在金融与物联网领域实现高效合规运营,带来的新机遇与挑战如何管理?
386
2023-07-22
Spring.Net在MVC中实现注入的原理解析
本文将介绍Spring.Net(不仅仅是Spring.Net,其实所有的IoC容器要向控制器中进行注入,原理都是差不多的)在MVC控制器中依赖注入的实现原理,本文并没有关于在MVC使用Spring怎么配置,怎么使用,怎么实现。
引言放在前面,只是为了避免浪费你的时间。
望你能静心片刻,认真阅读。
防止爬虫,加个链接:https://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 ProcessRequesYjcnYGlDt(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
39 public static EndInvokeVoidDelegate
40 public static Func
41
42 // Methods
43 static <>c();
44 public <>c();
45 internal IAsyncResult
46 internal void
47 internal bool
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的对象
总结
仔细品味每一行代码,会发现任何东西都没有表面上那么简单,每一个实现的背后都值得深入研究。
码了这么长时间,希望能对正在阅读的你有所帮助。
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~