這幾天利用空閑時間,我將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,異步與同步方法的執行有所不同,源代碼如下:

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); } }); }
通過上述代碼,我們知道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>(); } }
這個類沒有什么復雜的方法及屬性,只是實現了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); }
自定義實現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; } }
在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視圖代碼:

@{ 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="登 錄" /> }
最終的效果如下圖示: