深刻分析MVC中經過IOC實現Controller依賴注入的原理

這幾天利用空閒時間,我將ASP.NET反編譯後的源代碼並結合園子裏幾位大俠的寫的文章認真的看了一遍,收穫頗豐,同時也摘要了一些學習內容,存入了該篇文章:《ASP.NET運行機制圖解》,在對整個ASP.NET的運行機制有所瞭解後,我又對MVC的運行機制也進行了源碼分析,由於網上已經有不少的關於MVC實現原理的介紹,因此我這裏再也不重複討論這方面的內容,而主要講解一下Controller的的建立、執行以及如何實現依賴注入,注入的步驟是什麼?html

首先,咱們來看一下正常的Controller的的建立與執行順序:web

你們都應該知道,用於處理ASP.NET請求是由實現了IHttpHandler的對象來進行處理的,咱們所常見的Handler包括但不限於:Page,MvcHandler等安全

以下是MvcHandler類中的方法及執行步驟說明:mvc

處理入口方法:異步-->BeginProcessRequest,同步-->  ProcessRequest,源代碼以下:app

        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,源代碼以下:async

        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,異步與同步方法的執行有所不一樣,源代碼以下:ide

        protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
        {
            return SecurityUtil.ProcessInApplicationTrust<IAsyncResult>(delegate
            {
                IController controller;
                IControllerFactory factory;
                this.ProcessRequestInit(httpContext, out controller, out factory);
                IAsyncController asyncController = controller as IAsyncController;
                if (asyncController != null)
                {
                    BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState)
                    {
                        IAsyncResult result;
                        try
                        {
                            result = asyncController.BeginExecute(this.RequestContext, asyncCallback, asyncState);
                        }
                        catch
                        {
                            factory.ReleaseController(asyncController);
                            throw;
                        }
                        return result;
                    };
                    EndInvokeDelegate endDelegate = delegate(IAsyncResult asyncResult)
                    {
                        try
                        {
                            asyncController.EndExecute(asyncResult);
                        }
                        finally
                        {
                            factory.ReleaseController(asyncController);
                        }
                    };
                    SynchronizationContext synchronizationContext = SynchronizationContextUtil.GetSynchronizationContext();
                    AsyncCallback callback2 = AsyncUtil.WrapCallbackForSynchronizedExecution(callback, synchronizationContext);
                    return AsyncResultWrapper.Begin(callback2, state, beginDelegate, endDelegate, MvcHandler._processRequestTag);
                }
                Action action = delegate
                {
                    try
                    {
                        controller.Execute(this.RequestContext);
                    }
                    finally
                    {
                        factory.ReleaseController(controller);
                    }
                };
                return AsyncResultWrapper.BeginSynchronous(callback, state, action, MvcHandler._processRequestTag);
            });
        }


        protected internal virtual void EndProcessRequest(IAsyncResult asyncResult)
        {
            SecurityUtil.ProcessInApplicationTrust(delegate
            {
                AsyncResultWrapper.End(asyncResult, MvcHandler._processRequestTag);
            });
        }

        protected internal virtual void ProcessRequest(HttpContextBase httpContext)
        {
            SecurityUtil.ProcessInApplicationTrust(delegate
            {
                IController controller;
                IControllerFactory controllerFactory;
                this.ProcessRequestInit(httpContext, out controller, out controllerFactory);
                try
                {
                    controller.Execute(this.RequestContext);
                }
                finally
                {
                    controllerFactory.ReleaseController(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,而該類型是一個內部類:

        private class DefaultDependencyResolver : IDependencyResolver
        {
            public object GetService(Type serviceType)
            {
                object result;
                try
                {
                    result = Activator.CreateInstance(serviceType);
                }
                catch
                {
                    result = null;
                }
                return result;
            }

            public IEnumerable<object> GetServices(Type serviceType)
            {
                return Enumerable.Empty<object>();
            }
        }
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字段,從而最終實現咱們想要的結果。

        public static void SetResolver(IDependencyResolver resolver)
        {
            DependencyResolver._instance.InnerSetResolver(resolver);
        }

        public static void SetResolver(object commonServiceLocator)
        {
            DependencyResolver._instance.InnerSetResolver(commonServiceLocator);
        }

        public static void SetResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices)
        {
            DependencyResolver._instance.InnerSetResolver(getService, getServices);
        }

        public void InnerSetResolver(IDependencyResolver resolver)
        {
            if (resolver == null)
            {
                throw new ArgumentNullException("resolver");
            }
            this._current = resolver;
        }

        public void InnerSetResolver(object commonServiceLocator)
        {
            if (commonServiceLocator == null)
            {
                throw new ArgumentNullException("commonServiceLocator");
            }
            Type type = commonServiceLocator.GetType();
            MethodInfo method = type.GetMethod("GetInstance", new Type[]
            {
                typeof(Type)
            });
            MethodInfo method2 = type.GetMethod("GetAllInstances", new Type[]
            {
                typeof(Type)
            });
            if (method == null || method.ReturnType != typeof(object) || method2 == null || method2.ReturnType != typeof(IEnumerable<object>))
            {
                throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DependencyResolver_DoesNotImplementICommonServiceLocator, new object[]
                {
                    type.FullName
                }), "commonServiceLocator");
            }
            Func<Type, object> getService = (Func<Type, object>)Delegate.CreateDelegate(typeof(Func<Type, object>), commonServiceLocator, method);
            Func<Type, IEnumerable<object>> getServices = (Func<Type, IEnumerable<object>>)Delegate.CreateDelegate(typeof(Func<Type, IEnumerable<object>>), commonServiceLocator, method2);
            this._current = new DependencyResolver.DelegateBasedDependencyResolver(getService, getServices);
        }

        public void InnerSetResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices)
        {
            if (getService == null)
            {
                throw new ArgumentNullException("getService");
            }
            if (getServices == null)
            {
                throw new ArgumentNullException("getServices");
            }
            this._current = new DependencyResolver.DelegateBasedDependencyResolver(getService, getServices);
        }
View Code

 自定義實現IDependencyResolver接口的類型(採用Unity須要注意,使用常規的解析方法會存在錯誤,可參見DUDU的這篇文章《Unity+MVC:實現IDependencyResolver接口須要注意的地方》,我這裏直接借鑑Unity.MVC3裏面定義的類),如:

    public class CustomDependencyResolver:IDependencyResolver
    {
        private const string HttpContextKey = "perRequestContainer";

        private readonly IUnityContainer container;


        public CustomDependencyResolver()
        {
            this.container = BuildAndInitContainer();
        }

        public object GetService(Type serviceType)
        {
            if (typeof(IController).IsAssignableFrom(serviceType))
            {
                return ChildContainer.Resolve(serviceType);
            }

            return IsRegistered(serviceType) ? ChildContainer.Resolve(serviceType) : null;      
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            if (IsRegistered(serviceType))
            {
                yield return ChildContainer.Resolve(serviceType);
            }

            foreach (var service in ChildContainer.ResolveAll(serviceType))
            {
                yield return service;
            }
        }

        protected IUnityContainer ChildContainer
        {
            get
            {
                var childContainer = HttpContext.Current.Items[HttpContextKey] as IUnityContainer;

                if (childContainer == null)
                {
                    HttpContext.Current.Items[HttpContextKey] = childContainer = container.CreateChildContainer();
                }

                return childContainer;
            }
        } 

        public static void DisposeOfChildContainer()
        {
            var childContainer = HttpContext.Current.Items[HttpContextKey] as IUnityContainer;

            if (childContainer != null)
            {
                childContainer.Dispose();
            }
        }

        private bool IsRegistered(Type typeToCheck)
        {
            var isRegistered = true;

            if (typeToCheck.IsInterface || typeToCheck.IsAbstract)
            {
                isRegistered = ChildContainer.IsRegistered(typeToCheck);

                if (!isRegistered && typeToCheck.IsGenericType)
                {
                    var openGenericType = typeToCheck.GetGenericTypeDefinition();

                    isRegistered = ChildContainer.IsRegistered(openGenericType);
                }
            }

            return isRegistered;
        }

        private IUnityContainer BuildAndInitContainer()
        {
            var container = new UnityContainer();

            container.RegisterType<IUserService, UserService>();
            //這裏添加其它類型映射

            return container;
        }
    }
View Code

在Global文件的Application_Start方法中添加註入代碼,以下:

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

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

            DependencyResolver.SetResolver(new MvcApplication1.Models.CustomDependencyResolver());
        }

使用方法很簡單,以下是所有代碼:

    public interface IUserService
    {
        bool Login(string userName, string password, out string failureMsg);
    }

    public class UserService : IUserService
    {
        public bool Login(string userName, string password, out string failureMsg)
        {
            failureMsg = null;
            if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
            {
                failureMsg = "用戶名或密碼不能爲空!";
                return false;
            }

            if (userName != "admin" || password != "web.admin")
            {
                failureMsg = "用戶名或密碼不正確!";
                return false;
            }

            return true;
        }
    }


    public class UserController : Controller
    {
        private readonly IUserService userService;

        public UserController(IUserService service)
        {
            userService = service;
        }

        public ActionResult Login()
        {
            return View();
        }

        [HttpPost]
        [ActionName("Login")]
        public ActionResult LoginExecute(string username, string password)
        {
            string msg = null;
            if (!userService.Login(username, password,out msg))
            {
                return Content("<p style='color:red;'>登陸失敗,緣由以下:<br/>"+ msg +"</p>", "text/html", Encoding.UTF8);
            }
            return Content("<p style='color:green;'>登陸成功!</p>", "text/html", Encoding.UTF8); 
        }

    }
View Code

VIEW視圖代碼:

@{
    ViewBag.Title = "Login";
}

<h2>Login</h2>

@using(Html.BeginForm())
{
<p>
    <span>用戶名:</span>
    @Html.TextBox("username")
</p>
<p>
    <span>密 碼:</span>
    @Html.Password("password")
</p>
<input type="submit" value="登 錄" />
}
View Code

最終的效果以下圖示:

                        

                        

相關文章
相關標籤/搜索