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的相关所有信息。

posted @ 2013-02-17 14:02  肖&申&克  阅读(290)  评论(0编辑  收藏  举报