mvc源码解析(8)-ControllerDescriptor控制器描述对象
在上一篇讲完Controller对象创建,我们知道Controller对象是在MvcHandler里面的ProcessRequest方法里面调用的ProcessRequestInit来完成的,因此所有的初始化工作准备完成之后,开始执行Controller的激活工作。Controller的激活执行的是接口IController里面的Execute方法,该方法定义如下:
public interface IController { void Execute(RequestContext requestContext); } |
而最终的执行是在类Controller里面的ExecuteCore方法,该方法具体实现如下:
protected override void ExecuteCore() { PossiblyLoadTempData(); try { string actionName = RouteData.GetRequiredString("action"); if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) { HandleUnknownAction(actionName);} }finally { PossiblySaveTempData(); } } |
红色的代码InvokeAction是接口IActionInvoker里面的方法,该接口定义如下:
public interface IActionInvoker { bool InvokeAction(ControllerContext controllerContext, string actionName); } |
类ControllerActionInvoker实现了该接口,InvokeAction方法具体实现如下:
public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) { //获取Controller和Action的描述对象 ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext); ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName); if (actionDescriptor != null) { FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor); try { AuthorizationContext authContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor); if (authContext.Result != null) { InvokeActionResult(controllerContext, authContext.Result); } else { if (controllerContext.Controller.ValidateRequest) { ValidateRequest(controllerContext); } IDictionary<string, object> parameters = GetParameterValues(controllerContext, actionDescriptor); ActionExecutedContext postActionContext = InvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters); InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters, postActionContext.Result); } }catch (ThreadAbortException) {throw;} catch (Exception ex) { ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex); if (!exceptionContext.ExceptionHandled) {throw;} InvokeActionResult(controllerContext, exceptionContext.Result); } return true; } return false; } |
我们先来看看这一句代码:
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext); |
GetControllerDescriptor()方法到底是获取什么对象?该方法实现如下:
protected virtual ControllerDescriptor GetControllerDescriptor(ControllerContext controllerContext) { Type controllerType = controllerContext.Controller.GetType(); ControllerDescriptor controllerDescriptor = DescriptorCache.GetDescriptor(controllerType, () => new ReflectedControllerDescriptor(controllerType)); return controllerDescriptor; } |
从缓存中获取ControllerDescriptor对象,GetDescriptor()方法实现如下:
public ControllerDescriptor GetDescriptor(Type controllerType, Func<ControllerDescriptor> creator) { return FetchOrCreateItem(controllerType, creator); } |
这其中涉及到的FetchOrCreateItem()方法如下:
protected TValue FetchOrCreateItem(TKey key, Func<TValue> creator) { _rwLock.EnterReadLock(); try { TValue existingEntry; if (_cache.TryGetValue(key, out existingEntry)){return existingEntry;} }finally {_rwLock.ExitReadLock();} TValue newEntry = creator(); _rwLock.EnterWriteLock(); try { TValue existingEntry; if (_cache.TryGetValue(key, out existingEntry)) { return existingEntry; } _cache[key] = newEntry; return newEntry; }finally { _rwLock.ExitWriteLock(); } } |
该方法实现缓存的原理较为简单,结合_cache的定义:
private readonly Dictionary<TKey, TValue> _cache; |
我们可以知道先是在Dictionary字典中查看是否有要寻找的Key,有的话直接返回key对应的Value,如果没有,则将新的项插入到缓存中,以备下次调用。在上面的代码中获取的ControllerDescriptor对象其实是ReflectedControllerDescriptor对象,类ReflectedControllerDescriptor实现了抽象类ControllerDescriptor的方法。ReflectedControllerDescriptor类的构造函数如下:
public ReflectedControllerDescriptor(Type controllerType) { _controllerType = controllerType; _selector = new ActionMethodSelector(_controllerType); } |
ActionMethodSelector()方法里面最终调用的是PopulateLookupTables():
private void PopulateLookupTables() { MethodInfo[] allMethods = ControllerType.GetMethods(BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public); MethodInfo[] actionMethods = Array.FindAll(allMethods, IsValidActionMethod); AliasedMethods = Array.FindAll(actionMethods, IsMethodDecoratedWithAliasingAttribute); NonAliasedMethods = actionMethods.Except(AliasedMethods).ToLookup(method => method.Name, StringComparer.OrdinalIgnoreCase); } |
结合方法IsValidActionMethod(),该方法定义如下:
private static bool IsValidActionMethod(MethodInfo methodInfo) { return !(methodInfo.IsSpecialName || methodInfo.GetBaseDefinition().DeclaringType.IsAssignableFrom(typeof(Controller))); } |
因此我们可以知道PopulateLookupTables就是获取到所有公有的Action方法,并将寻找到的Action方法分为有别名的Action和没有别名的Action。同时我们来看看ControllerDescriptor类里面的一些成员变量:
public virtual string ControllerName { get {string typeName = ControllerType.Name; if (typeName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)) { return typeName.Substring(0, typeName.Length - "Controller".Length); } return typeName;} } public abstract Type ControllerType {get;} public virtual string UniqueId {get {return _uniqueId.Value;}} |
因此我们可以知道ControllerDescriptor用来描述某一个Controller的元数据,其中ControllerName表示Controller的名称,来源于路由信息,ControllerType之前我们已经讲过,表示路由的类型,说白了就是可以区分具体的Controller对象(包含了命名空间等信息)。string类型的UniqueId用于标识唯一的ControllerDescriptor对象。同时我们还应该注意到ControllerDescriptor里面还有两个十分重要的抽象方法:
//ControllerDescriptor的FindAction方法根据指定的Controller上下文和名称得到相应的Action方法,返回的是用于描述Action方法的ActionDescriptor对象 public abstract ActionDescriptor FindAction(ControllerContext controllerContext, string actionName); public abstract ActionDescriptor[] GetCanonicalActions(); |
这两个方法都在ControllerDescriptor的实现类ReflectedControllerDescriptor里面进行了重写,我们先来看看FindAction()方法的具体实现:
public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName) { MethodInfo matched = _selector.FindActionMethod(controllerContext, actionName); if (matched == null) {return null;} return new ReflectedActionDescriptor(matched, actionName, this); } |
里面最终调用的就是ReflectedActionDescriptor这个类的构造函数:
internal ReflectedActionDescriptor(MethodInfo methodInfo, string actionName, ControllerDescriptor controllerDescriptor, bool validateMethod) { if (validateMethod) {string failedMessage = VerifyActionMethodIsCallable(methodInfo);} MethodInfo = methodInfo; _actionName = actionName; _controllerDescriptor = controllerDescriptor; _uniqueId = new Lazy<string>(CreateUniqueId); } |
GetCanonicalActions()方法就是获取到Controller上的所有的公有的Action方法。这个获取Controller上的Action方法是有一些限制的,这些限制在VerifyActionMethodIsCallable方法有说明:1:不能是静态的方法;2:不能是继承自基类的方法,3:不能是开放的泛型类型参数;4:方法的参数中不能有ref和out修饰。
internal static string VerifyActionMethodIsCallable(MethodInfo methodInfo) { // we can't call static methods if (methodInfo.IsStatic) {return String.Format(CultureInfo.CurrentCulture,MvcResources.ReflectedActionDescriptor_CannotCallStaticMethod,methodInfo,methodInfo.ReflectedType.FullName);} // we can't call instance methods where the 'this' parameter is a type other than ControllerBase if (!typeof(ControllerBase).IsAssignableFrom(methodInfo.ReflectedType)) {return String.Format(CultureInfo.CurrentCulture,MvcResources.ReflectedActionDescriptor_CannotCallInstanceMethodOnNonControllerType, methodInfo,methodInfo.ReflectedType.FullName);} // we can't call methods with open generic type parameters if (methodInfo.ContainsGenericParameters) {return String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_CannotCallOpenGenericMethods,methodInfo, methodInfo.ReflectedType.FullName);} // we can't call methods with ref/out parameters ParameterInfo[] parameterInfos = methodInfo.GetParameters(); foreach (ParameterInfo parameterInfo in parameterInfos) { if (parameterInfo.IsOut || parameterInfo.ParameterType.IsByRef) {return String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedActionDescriptor_CannotCallMethodsWithOutOrRefParameter,methodInfo, methodInfo.ReflectedType.FullName, parameterInfo); } } //we can call this method return null; } |
上面的废话讲了那么多,其实我们只要了解ControllerDescriptor里面包含着什么样的信息即可,如上面红色的语言所讲,说白了就是某一个Controller的包装类,里面包含有关Controller的相关所有信息。