ASP.NET MVC 3.0 源码阅读手记(1)
前言:本文或许不太适合大众阅读,这只是本人查看ASP.NET MVC 3.0源码时记的流水账,文中夹杂诸多未经证实的猜想,请自行采纳。文中亦摘录了很多代码,主要是为了方便以后阅读时懒得再翻看对照源码,特此声明。
在阅读源码前先了解下MVC的大致处理流程还是很有必要的,网上比较出名的是Steven Sanderson提供的一份请求处理流程图(原文:请猛击这里)。
因为在使用ASP.NET MVC开发的时候,我们首先接触到的是路由,亦传说中的 System.Web.Routing.RouteTable,Reflection该类的代码如下:
private static RouteCollection _instance = new RouteCollection(); public static RouteCollection Routes { [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get { return RouteTable._instance; } }
好吧,这么的单纯简单,那就去看看RouteCollection的代码吧,发现该类中并无常用的那个MapRoute方法,原来该方法是MVC框架扩展的,具体代码位于MVC项目源码中的 System.Web.Mvc.RouteCollectionExtensions 类,下面是其代码:
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
{ if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } Route route = new Route(url, new MvcRouteHandler()) { Defaults = new RouteValueDictionary(defaults), Constraints = new RouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens["Namespaces"] = namespaces; } routes.Add(name, route); return route; }
以上代码虽简单但有两处重点:
- 创建了一个Route对象,在其构造函数中传入了MvcRouteHandler对象;
- 设置了新建Route对象的Default、Constraints、DataTokens这三个RouteValueDictionary类型的属性,其中默认值(Defaults)和约束(Constraints)是通过object的对象进行初始化的,关于RouteValueDictionary类型是以string为键object为值的字典,具体代码这里就不贴了,大家可以反射看看。
我们只要看看RouteValueDictionary这个构造函数以及私有的AddValues方法即可:
public RouteValueDictionary(object values) { this._dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); this.AddValues(values); }
private void AddValues(object values) { if (values != null) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(values); foreach (PropertyDescriptor propertyDescriptor in properties) { object value = propertyDescriptor.GetValue(values); this.Add(propertyDescriptor.Name, value); } } }
上面的AddValues方法利用了TypeDescriptor来获取values参数指定的任意对象的公共属性集,并将这些属性值以名称/值的方式加入到内部字典中,这对于使用匿名类进行设置Route对象中的相关属性非常方便。在MapRoute方法中,使用的是MvcRouteHandler的默认构造函数来创建它的实例,那么就顺藤摸瓜吧:
public class MvcRouteHandler : IRouteHandler { private IControllerFactory _controllerFactory; public MvcRouteHandler() { } public MvcRouteHandler(IControllerFactory controllerFactory) { _controllerFactory = controllerFactory; } protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) { requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext)); return new MvcHandler(requestContext); } protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext) { string controllerName = (string)requestContext.RouteData.Values["controller"]; IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory(); return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName); } #region IRouteHandler Members IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) { return GetHttpHandler(requestContext); } #endregion }
在GetHttpHandler方法最后返回了一个新建的MvcHandler对象,这个类先按着不表。先看看关于控制器工厂的问题,由于默认构造函数保留了_controllerFactory成员变量为空(null),因此GetSessionStateBehavior方法中是通过ControllerBuilder来获取控制器工厂对象的,且看看它是如何获取的:
public class ControllerBuilder { private Func<IControllerFactory> _factoryThunk = () => null; private static ControllerBuilder _instance = new ControllerBuilder(); private HashSet<string> _namespaces = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private IResolver<IControllerFactory> _serviceResolver; public ControllerBuilder() : this(null) { } internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver) { _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>( () => _factoryThunk(), new DefaultControllerFactory { ControllerBuilder = this }, "ControllerBuilder.GetControllerFactory" ); } public static ControllerBuilder Current { get { return _instance; } } public HashSet<string> DefaultNamespaces { get { return _namespaces; } } public IControllerFactory GetControllerFactory() { return _serviceResolver.Current; } public void SetControllerFactory(IControllerFactory controllerFactory) { if(controllerFactory == null) { throw new ArgumentNullException("controllerFactory"); } _factoryThunk = () => controllerFactory; } public void SetControllerFactory(Type controllerFactoryType) { if(controllerFactoryType == null) { throw new ArgumentNullException("controllerFactoryType"); } if(!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType)) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_MissingIControllerFactory, controllerFactoryType), "controllerFactoryType"); } _factoryThunk = delegate() { try { return (IControllerFactory)Activator.CreateInstance(controllerFactoryType); } catch(Exception ex) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_ErrorCreatingControllerFactory, controllerFactoryType), ex); } }; } }
大致看看,这个类主要就是用来获取IControllerFactory对象的,其中GetControllerFactory方法很简单,通过内部的IResolver<IControllerFactory>来解析得到想要的IControllerFactory服务,其中两个重载的SetControllerFactory注入方法的实现比较简单,只是刚开始有点奇怪为什么不直接通过将注入的IControllerFactory类型的controllerFactory参数保存起来作为GetControllerFactory的返回之用,还非得绕个弯弯通过_factoryThunk 这个委托进行返回?没事,看到后面就知道为什么了,据说这也是MVC 3.0中为了扩展性加入的新处理方式(至于听谁说的…)。既然ControllerBuilder把获取IControllerFactory的职责都交给了默认的SingleServiceResolver类,那么就看看它是咋么一回事吧!
internal class SingleServiceResolver<TService> : IResolver<TService> where TService : class { private TService _currentValueFromResolver; private Func<TService> _currentValueThunk; private TService _defaultValue; private Func<IDependencyResolver> _resolverThunk; private string _callerMethodName; public SingleServiceResolver(Func<TService> currentValueThunk, TService defaultValue, string callerMethodName) { if(currentValueThunk == null) throw new ArgumentNullException("currentValueThunk"); if(defaultValue == null) throw new ArgumentNullException("defaultValue"); _resolverThunk = () => DependencyResolver.Current; _currentValueThunk = currentValueThunk; _defaultValue = defaultValue; _callerMethodName = callerMethodName; } public TService Current { get { if(_resolverThunk != null) { lock(_currentValueThunk) { if(_resolverThunk != null) { _currentValueFromResolver = _resolverThunk().GetService<TService>(); _resolverThunk = null; if(_currentValueFromResolver != null && _currentValueThunk() != null) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, MvcResources.SingleServiceResolver_CannotRegisterTwoInstances, typeof(TService).Name.ToString(), _callerMethodName)); } } } } return _currentValueFromResolver ?? _currentValueThunk() ?? _defaultValue; } } }
在SingleServiceResolver类的Current属性中,首先是判断_resolverThunk成员变量是否为空,而这个变量是返回IDependencyResolver接口的一个委托,在构造函数中可以看到它指向DependencyResolver类的静态属性Current(这个东东先且放放),紧接着就是标准的双检锁的处理,具体工作代码是通过这个委托获取到的IDependcencyResolver的GetService方法来获取IControllerFactory,获取到之后马上将该委托置空,然后判断获取到的控制器工厂对象是否不为空并且通过构造函数传入的那个“绕弯弯”的返回IControllerFactory的委托返回的控制器工厂是否也不为空,如果都不为空则表示获取方式有冲突,变抛出一个无效操作异常,该异常表示不能注册两种获取服务实例的方式。
最后一行是返回代码,这里有蹊跷,它表明了对几种获取服务方式的优先级:
- 优先使用IDependencyResolver的GetService方法的结果;
- 其次使用构造函数传入的Func<IControllerFactory>类型参数的委托返回结果;
- 最后才使用构造函数传入的具体的IControllerFactory类型的控制器工厂对象。
看到这里,是不是有些眼熟?貌似跟很多IoC框架中的那个ServiceLocator有点神似呢?不扯这个了,既然弄清楚了优先级别,那么根据构造函数的代码,这个优先级别最高的依赖解析器(IDependencyResolver)指向了DependencyResolver的Current静态属性,那么就去围观下它吧:
public class DependencyResolver { private static DependencyResolver _instance = new DependencyResolver(); public static IDependencyResolver Current { get { return _instance.InnerCurrent; } } private IDependencyResolver _current = new DefaultDependencyResolver(); public IDependencyResolver InnerCurrent { get { return _current; } } public void InnerSetResolver(IDependencyResolver resolver) { if(resolver == null) throw new ArgumentNullException("resolver"); _current = resolver; } public void InnerSetResolver(object commonServiceLocator) { if(commonServiceLocator == null) throw new ArgumentNullException("commonServiceLocator"); Type locatorType = commonServiceLocator.GetType(); MethodInfo getInstance = locatorType.GetMethod("GetInstance", new[] { typeof(Type) }); MethodInfo getInstances = locatorType.GetMethod("GetAllInstances", new[] { typeof(Type) }); if(getInstance == null || getInstance.ReturnType != typeof(object) || getInstances == null || getInstances.ReturnType != typeof(IEnumerable<object>)) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, MvcResources.DependencyResolver_DoesNotImplementICommonServiceLocator, locatorType.FullName ), "commonServiceLocator" ); } var getService = (Func<Type, object>)Delegate.CreateDelegate(typeof(Func<Type, object>), commonServiceLocator, getInstance); var getServices = (Func<Type, IEnumerable<object>>)Delegate.CreateDelegate(typeof(Func<Type, IEnumerable<object>>), commonServiceLocator, getInstances); _current = new 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"); _current = new DelegateBasedDependencyResolver(getService, getServices); }
private class DelegateBasedDependencyResolver : IDependencyResolver { Func<Type, object> _getService; Func<Type, IEnumerable<object>> _getServices; public DelegateBasedDependencyResolver(Func<Type, object> getService, Func<Type, IEnumerable<object>> getServices) { _getService = getService; _getServices = getServices; } public object GetService(Type type) { try { return _getService.Invoke(type); } catch { return null; } } public IEnumerable<object> GetServices(Type type) { return _getServices(type); } }
private class DefaultDependencyResolver : IDependencyResolver { public object GetService(Type serviceType) { try { return Activator.CreateInstance(serviceType); } catch { return null; } } public IEnumerable<object> GetServices(Type serviceType) { return Enumerable.Empty<object>(); } } }
这个类的代码个人感觉写得有点矬,有几个简单的静态注册服务的方法就没贴上来了。这个类完全可以写成静态类,属于IDependencyResolver的Facade,其中InnerCurrent属性为获取IDependencyResolver服务,而其他三个InnerSetResolver重载方法分别为不同的注册服务方法,其中第一个很简单,第二个为Common Service Locator模式的注册,第三个以委托的方式进行动态注册。里面两个嵌套类都很容易看懂就不做说明。需要留意的是,当前默认解析器为DefaultDependencyResolver对象,而该对象只是简单的根据服务类型进行反射创建而已。综合上面几个类的代码,跟踪至此我们可以确定,在默认情况下只是由DefaultDependencyResolver类的GetService方法发射创建了制定类型的实例而已。
且慢,在SingleServiceResolver类的Current属性的getter中,有这行代码:_currentValueFromResolver = _resolverThunk().GetService<TService>();而这个TService泛型参数由ControllerBuilder类的构造函数中指定,且看:_serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(...); 即 TService 为 IControllerFactory,这样自然由_resolverThunk委托返回的DefaultDependencyResolver对象的GetService(Type serviceType)方法是不能正常工作的,故而被里面的catch捕获而返回空。注意上面的GetService<TService>()泛型方法其实由DependencyResolverExtensions类中定义的扩展方法,但是该扩展方法只是简单调用同名非泛型的原方法而已,故此不做展开。由此回溯,最后还是返回由ControllerBuilder构造函数中所指定的那个控制器工厂对象,即DefaultControllerFactory类实例。
好了,绕了这么一大圈子,结论就是在MVC 3中如果不进行服务替换,默认的控制器工厂就是这个的System.Web.Mvc.DefaultControllerFactory类。虽然这里白绕了一圈,但是也算基本弄明白了它的扩展机制,因为在其他地方都有用到IResolver<T>和IDependencyResolver的。好吧,先看到这…