深入分析MVC中通过IOC实现Controller依赖注入的原理

  1. 云栖社区>
  2. 博客>
  3. 正文

深入分析MVC中通过IOC实现Controller依赖注入的原理

技术小牛人 2017-11-21 18:34:00 浏览686
展开阅读全文

这几天利用空闲时间,我将ASP.NET反编译后的源代码并结合园子里几位大侠的写的文章认真的看了一遍,收获颇丰,同时也摘要了一些学习内容,存入了该篇文章:《ASP.NET运行机制图解》,在对整个ASP.NET的运行机制有所了解后,我又对MVC的运行机制也进行了源码分析,因为网上已经有很多的关于MVC实现原理的介绍,所以我这里不再重复讨论这方面的内容,而主要讲解一下Controller的的创建、执行以及如何实现依赖注入,注入的步骤是什么?

首先,我们来看一下正常的Controller的的创建与执行顺序:

大家都应该知道,用于处理ASP.NET请求是由实现了IHttpHandler的对象来进行处理的,我们所常见的Handler包括但不限于:Page,MvcHandler等

如下是MvcHandler类中的方法及执行步骤说明:

处理入口方法:异步-->BeginProcessRequest,同步-->  ProcessRequest,源代码如下:

复制代码
        IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            return this.BeginProcessRequest(context, cb, extraData);
        }

        void IHttpHandler.ProcessRequest(HttpContext httpContext)
        {
            this.ProcessRequest(httpContext);
        }
复制代码

注意这两个方法是显示实现IHttpHandler的同名方法的,不能直接调用,必需转换成IHttpHandler类型后才能调用,调用转到如下方法:

复制代码
        protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)
        {
            HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
            return this.BeginProcessRequest(httpContext2, callback, state);
        }

        protected virtual void ProcessRequest(HttpContext httpContext)
        {
            HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
            this.ProcessRequest(httpContext2);
        }
复制代码

当然这两个方法均又分别调动了各自的重载方法,在重载方法中都调用了ProcessRequestInit,源代码如下:

复制代码
        private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
        {
            if (ValidationUtility.IsValidationEnabled(HttpContext.Current) == true)
            {
                ValidationUtility.EnableDynamicValidation(HttpContext.Current);
            }
            this.AddVersionHeader(httpContext);
            this.RemoveOptionalRoutingParameters();
            string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
            factory = this.ControllerBuilder.GetControllerFactory();
            controller = factory.CreateController(this.RequestContext, requiredString);
            if (controller == null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]
                {
                    factory.GetType(),
                    requiredString
                }));
            }
        }
复制代码

红色标明的就是创建Controller的地方,创建完后就开始执行Controller,异步与同步方法的执行有所不同,源代码如下:

 View Code

 通过上述代码,我们知道Controller执行步骤是:异步(BeginExecute-->EndExecute-->ReleaseController),同步(Execute-->  ReleaseController)

我们知道了Controller的创建与执行原理,就可以针对这些代码规则来扩展我们自定义的一些实现代码,比如我们本文要讲的:通过IOC实现Controller依赖注入。

通过上面源代码的分析,我们知道,Controller是由ControllerFactory来创建的,而ControllerFactory又是由ControllerBuilder,也就是我们只要能够改变ControllerBuilder.GetControllerFactory返回的值,也就改变了ControllerFactory,这样就给自定义创建Controller提供可能,我们先来看一下,ControllerBuilder.GetControllerFactory方法的定义:

public IControllerFactory GetControllerFactory()
{
    return this._serviceResolver.Current;
}

方法很简单,直接通过IResolver<IControllerFactory>.Current返回实现了IControllerFactory对象,而对于方法中的_serviceResolver是在构造函数中实例化的,源代码如下:

复制代码
        internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
        {
            IResolver<IControllerFactory> arg_6A_1 = serviceResolver;
            if (serviceResolver == null)
            {
                arg_6A_1 = new SingleServiceResolver<IControllerFactory>(() => this._factoryThunk(), new DefaultControllerFactory
                {
                    ControllerBuilder = this
                }, "ControllerBuilder.GetControllerFactory");
            }
            this._serviceResolver = arg_6A_1;
        }
复制代码

通过构造函数,可以看出_serviceResolver是由SingleServiceResolver<IControllerFactory>实例化得来的,那么结合上面的this._serviceResolver.Current,就可以知道其实就是访问SingleServiceResolver<IControllerFactory>的Current属性来获得IControllerFactory对象,源代码如下:

复制代码
        public TService Current
        {
            get
            {
                if (this._resolverThunk != null)
                {
                    lock (this._currentValueThunk)
                    {
                        if (this._resolverThunk != null)
                        {
                            this._currentValueFromResolver = this._resolverThunk().GetService<TService>();
                            this._resolverThunk = null;
                            if (this._currentValueFromResolver != null && this._currentValueThunk() != null)
                            {
                                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.SingleServiceResolver_CannotRegisterTwoInstances, new object[]
                                {
                                    typeof(TService).Name.ToString(),
                                    this._callerMethodName
                                }));
                            }
                        }
                    }
                }
                TService arg_D2_0;
                if ((arg_D2_0 = this._currentValueFromResolver) == null && (arg_D2_0 = this._currentValueThunk()) == null)
                {
                    arg_D2_0 = this._defaultValue;
                }
                return arg_D2_0;
            }
        }
复制代码

SingleServiceResolver构造函数如下:

复制代码
        public SingleServiceResolver(Func<TService> currentValueThunk, TService defaultValue, string callerMethodName)
        {
            if (currentValueThunk == null)
            {
                throw new ArgumentNullException("currentValueThunk");
            }
            if (defaultValue == null)
            {
                throw new ArgumentNullException("defaultValue");
            }
            this._resolverThunk = (() => DependencyResolver.Current);
            this._currentValueThunk = currentValueThunk;
            this._defaultValue = defaultValue;
            this._callerMethodName = callerMethodName;
        }
复制代码

这里我们结合ControllerBuilder的构造函数及SingleServiceResolver构造函数得知在Current属性的逻辑代码中if (this._currentValueFromResolver != null && this._currentValueThunk() != null)是不成立的,因为this._currentValueThunk = currentValueThunk;而currentValueThunk又是ControllerBuilder中默认的值: Func<IControllerFactory> _factoryThunk = () => null;所以就会走到arg_D2_0 = this._defaultValue,而_defaultValue又是等于ControllerBuilder构造函数中传来的DefaultControllerFactory,所以最终返回了DefaultControllerFactory,如果需要实现自定义的ControllerFactory并且能够被ControllerBuilder.GetControllerFactory返回,我们只需要自定义实现IControllerFactory的类,如:CustomControllerFactory,以及使用ControllerBuilder.SetControllerFactory方法来使_factoryThunk 的值等于()=>CustomControllerFactory即可,实现的代码如下:

复制代码
    public class CustomControllerFactory : DefaultControllerFactory
    {
        private UnityContainer iocContainer;

        public CustomControllerFactory()
        {
            iocContainer = new UnityContainer();
            AddBindings();
        }

        protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
        {
            IController controller=null;
            if (controllerType != null)
            {
                controller = (IController)iocContainer.Resolve(controllerType);
            }
            return controller;
        }

        private void AddBindings()
        {
            iocContainer.RegisterType<IUserService, UserService>();
        }
         
    }
复制代码

我这里采用Unity容器,并重写了GetControllerInstance方法,如下代码是实现注入CustomControllerFactory到ControllerBuilder:

复制代码
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
        }
复制代码

这样我们就实现了返回自己定义的CustomControllerFactory,并在CustomControllerFactory通过IOC容器来实现自己所需要的Controller。

以上虽然通过自定义ControllerFactory实现了IOC的注入,但我仍然觉得有些烦锁,且存在不安全性,因为自己实现的CustomControllerFactory是可以去重写、覆盖改变DefaultControllerFactory中的相应的属性方法,如果自己写的代码存在漏洞或不健全,则会造成无法预料的后果,因此一般不建议直接这样做,而应该采用风险更小的其它方法来实现,那是什么方法呢?请继续往下看。

通过分析源码得知,默认情况下,Controller是由DefaultControllerFactory.GetControllerInstance得来的,那我们先来看看这个方法定义:

复制代码
        protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
            {
                throw new HttpException(404, string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound, new object[]
                {
                    requestContext.HttpContext.Request.Path
                }));
            }
            if (!typeof(IController).IsAssignableFrom(controllerType))
            {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase, new object[]
                {
                    controllerType
                }), "controllerType");
            }
            return this.ControllerActivator.Create(requestContext, controllerType);
        }
复制代码

注意红色标注的地方,这里是通过ControllerActivator属性对象来创建的,ControllerActivator属性定义如下:

复制代码
        private IControllerActivator ControllerActivator
        {
            get
            {
                if (this._controllerActivator != null)
                {
                    return this._controllerActivator;
                }
                this._controllerActivator = this._activatorResolver.Current;
                return this._controllerActivator;
            }
        }
复制代码

由此可知,ControllerActivator属性又是(私有字段:_activatorResolver) IResolver<IControllerActivator>.Current得来的,那么这个_activatorResolver又是如何得来的呢?通过上下源码的分析,得知,DefaultControllerFactory是在ControllerBuilder中构造的,那么我看一下DefaultControllerFactory的构造函数:

复制代码
        public DefaultControllerFactory() : this(null, null, null)
        {
        }

        internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver)
        {
            if (controllerActivator != null)
            {
                this._controllerActivator = controllerActivator;
                return;
            }
            IResolver<IControllerActivator> arg_44_1 = activatorResolver;
            if (activatorResolver == null)
            {
                arg_44_1 = new SingleServiceResolver<IControllerActivator>(() => null, new DefaultControllerFactory.DefaultControllerActivator(dependencyResolver), "DefaultControllerFactory contstructor");
            }
            this._activatorResolver = arg_44_1;
        }
复制代码

ControllerBuilder的构造函数中是采用的无参构造函数,而无参构造函数最终均会调用带有三个参数的构造函数,在这个函数中,我标出了重点需要关注的地方,有没有发现眼熟的地方,对的SingleServiceResolver又出现了,只不过泛型参数不同而已,我在上面分析时用绿色标记出了重要的地方:构造函数中的this._resolverThunk = (() => DependencyResolver.Current);以及Current属性中的this._currentValueFromResolver = this._resolverThunk().GetService<TService>();认真分析得知, DependencyResolver.Current是一个静态属性,看一下该属性的定义:

复制代码
public static IDependencyResolver Current
{
    get
    {
        return DependencyResolver._instance.InnerCurrent;
    }
}
复制代码

该属性的值来源于一个私有字段,这个字段是静态的并默认就实例化为DependencyResolver:

private static DependencyResolver _instance = new DependencyResolver();

得出结论DependencyResolver.Current调用DependencyResolver.InnerCurrent属性,而该属性又直接返回_current字段的值,代码如下:

复制代码
private IDependencyResolver _current = new DependencyResolver.DefaultDependencyResolver();

        public IDependencyResolver InnerCurrent
        {
            get
            {
                return this._current;
            }
        }
复制代码

_current字段默认是实例化DependencyResolver.DefaultDependencyResolver,而该类型是一个内部类:

 View Code

这个类没有什么复杂的方法及属性,只是实现了IDependencyResolver接口的两个方法,分别是  GetService、  GetServices(该方法返回空集合,即无用)。

到此一切就都明了了,如果说想要实现通过DefaultControllerFactory.GetControllerInstance来返回我们IOC注入后的Controller,只需要改变  SingleServiceResolver.Current属性返回值,而改变该值则需要改变该类的_resolverThunk字段值,而_resolverThunk的值又来自构造函数中的如下语句:

this._resolverThunk = (() => DependencyResolver.Current);

最终我们只要改变DependencyResolver.Current属性返回值即可,而该值通过上面的分析知道是来自DependencyResolver的字段:_current,所以只要改变这个字段的值,就能改变最终返回Controller实例对象。那如何改变这个私有字段呢?不急,通过源码代码得知,DependencyResolver提供了SetResolver多个静态重载方法,我们只需要将实现了IDependencyResolver接口实例对象传入进去,就可以改变_current字段,从而最终实现我们想要的结果。

 View Code

 自定义实现IDependencyResolver接口的类型(采用Unity需要注意,使用常规的解析方法会存在错误,可参见DUDU的这篇文章《Unity+MVC:实现IDependencyResolver接口需要注意的地方》,我这里直接借鉴Unity.MVC3里面定义的类),如:

 View Code

在Global文件的Application_Start方法中添加注入代码,如下:

复制代码
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            DependencyResolver.SetResolver(new MvcApplication1.Models.CustomDependencyResolver());
        }
复制代码

使用方法很简单,如下是全部代码:

 View Code

VIEW视图代码:

 View Code

最终的效果如下图示:

                        

                        

本文转自 梦在旅途 博客园博客,原文链接: http://www.cnblogs.com/zuowj/p/4958591.html ,如需转载请自行联系原作者

网友评论

登录后评论
0/500
评论
技术小牛人
+ 关注