好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Controller的激活

Controller的激活

上文说到Routing Module将控制权交给了MvcHandler,因为MvcHandler实现了IHttpAsyncHandler接口,因此紧接着就会调用BeginProcessRequest方法,这个方法首先会进行一些Trust Level之类的安全检测,暂且不谈,然后会调用ProcessRequestInit方法(有删节):

 private void   ProcessRequestInit(  HttpContextBase   httpContext,   out   IController   controller,   out   IControllerFactory   factory) {
              // Get the controller type
              string   controllerName = RequestContext.RouteData.GetRequiredString(  "controller"  );
              // Instantiate the controller and call Execute
              factory = ControllerBuilder.GetControllerFactory();
            controller = factory.CreateController(RequestContext, controllerName);
              if   (controller ==   null  ) {
                  throw new   InvalidOperationException  (
                      String  .Format(
                          CultureInfo  .CurrentCulture,
                          MvcResources  .ControllerBuilder_FactoryReturnedNull,
                        factory.GetType(),
                        controllerName));
            }
        } 

首先会获得controller的名字,然后会实例化controller,这里采用了抽象工厂的模式,首先利用ControllerBuilder获得一个IControllerFactory的实例,ControllerBuilder采用Dependency Injection来实例化IControllerFactory,关于MVC中DI的实现以后另文介绍,在默认情况下,ControllerBuilder会返回一个实例。接着调用CreateController方法:

 public virtual   IController   CreateController(  RequestContext   requestContext,   string   controllerName) {
  Type   controllerType = GetControllerType(requestContext, controllerName);
              IController   controller = DefaultControllerFactoryGetControllerInstance(requestContext, controllerType);
              return   controller;
        } 

方法分为两步,先获得类型,再获得实例:

 protected internal virtual   Type   GetControllerType(  RequestContext   requestContext,   string   controllerName) {
  // first search in the current route's namespace collection
              object   routeNamespacesObj;
              Type   match;
              if   (requestContext !=   null   && requestContext.RouteData.DataTokens.TryGetValue(  "Namespaces"  ,   out   routeNamespacesObj)) {
                  IEnumerable  <  string  > routeNamespaces = routeNamespacesObj   as   IEnumerable  <  string  >;
                  if   (routeNamespaces !=   null   && routeNamespaces.Any()) {
                      HashSet  <  string  > nsHash =   new   HashSet  <  string  >(routeNamespaces,   StringComparer  .OrdinalIgnoreCase);
                    match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsHash);

                      // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
                      if   (match !=   null   ||   false  .Equals(requestContext.RouteData.DataTokens[  "UseNamespaceFallback"  ])) {
                          // got a match or the route requested we stop looking
                          return   match;
                    }
                }
            }
              // then search in the application's default namespace collection
              if   (ControllerBuilder.DefaultNamespaces.Count > 0) {
                  HashSet  <  string  > nsDefaults =   new   HashSet  <  string  >(ControllerBuilder.DefaultNamespaces,   StringComparer  .OrdinalIgnoreCase);
                match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsDefaults);
                  if   (match !=   null  ) {
                      return   match;
                }
            }
              // if all else fails, search every namespace
              return   GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName,   null   /* namespaces */  );
        } 

DefaultControllerFactory在根据路由信息查找对应Controller的类型的时候,首先判断DataToken中有没有Namespace,然后调用GetControllerTypeWithinNamespaces 方法查找Controller对应的类。先看下这个方法:

 private   Type   GetControllerTypeWithinNamespaces(  RouteBase   route,   string   controllerName,   HashSet  <  string  > namespaces) {
              // Once the master list of controllers has been created we can quickly index into it
              ControllerTypeCache.EnsureInitialized(BuildManager);

              ICollection  <  Type  > matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
              switch   (matchingTypes.Count) {
                  case   0:
                      // no matching types
                      return null  ;

                  case   1:
                      // single matching type
                      return   matchingTypes.First();

                  default  :
                      // multiple matching types
                      throw   CreateAmbiguousControllerException(route, controllerName, matchingTypes);
            }
        } 

ASP.NET中大量的用到了反射,因此也需要把这些反射出的类进行缓存以提高性能,首先看下EnsureInitialized这个比较有意思的方法,这个方法的参数BuildManager经过了层层包装,其实只是System.Web.Compilation.BuildManager的一个实例。

 public void   EnsureInitialized(  IBuildManager   buildManager) {
              if   (_cache ==   null  ) {
                  lock   (_lockObj) {
                      if   (_cache ==   null  ) {
                          List  <  Type  > controllerTypes =   TypeCacheUtil  .GetFilteredTypesFromAssemblies(_typeCacheName, IsControllerType, buildManager);
                          var   groupedByName = controllerTypes.GroupBy(
                            t => t.Name.Substring(0, t.Name.Length -   "Controller"  .Length),
                              StringComparer  .OrdinalIgnoreCase);
                        _cache = groupedByName.ToDictionary(
                            g => g.Key,
                            g => g.ToLookup(t => t.Namespace ??   String  .Empty,   StringComparer  .OrdinalIgnoreCase),
                              StringComparer  .OrdinalIgnoreCase);
                    }
                }
            }
        } 

首先 TypeCacheUtil 获得所有是Controller的类型。TypeCacheUtil在 前文 已经出现过,用来获取所有的AreaRegistration的子类型,这里仔细看下这个方法:

 public static   List  <  Type  > GetFilteredTypesFromAssemblies(  string   cacheName,   Predicate  <  Type  > predicate,   IBuildManager   buildManager) {
              TypeCacheSerializer   serializer =   new   TypeCacheSerializer  ();

              // first, try reading from the cache on disk
              List  <  Type  > matchingTypes = ReadTypesFromCache(cacheName, predicate, buildManager, serializer);
              if   (matchingTypes !=   null  ) {
                  return   matchingTypes;
            }
              // if reading from the cache failed, enumerate over every assembly looking for a matching type
              matchingTypes = FilterTypesInAssemblies(buildManager, predicate).ToList();
              // finally, save the cache back to disk
              SaveTypesToCache(cacheName, matchingTypes, buildManager, serializer);
              return   matchingTypes;
        } 

这个方法会从缓存中读取controller类型的名字,缓存是存在一个文本文件中的,名字就是cacheName,在这里是Mvc-ControllerTypeCache.xml.这个文件的内容是如下样子的:

<?xml version="1.0" encoding="utf-8"?> 
<!--This file is automatically generated. Please do not modify the contents of this file.--> 
<typeCache lastModified="11/4/2012 8:52:26 PM" mvcVersionId="a5d58bd9-3a4a-4d1d-a7ce-9cef11e4c380"> 
  <assembly name="MVCApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> 
    <module versionId="c7b3d847-7853-44f3-87d0-9cc040c4cb53"> 
      <type>MVCApp.Areas.Admin.Controllers.HomeController</type> 
      <type>MVCApp.Controllers.HomeController</type> 
    </module> 
  </assembly> 
</typeCache>

再看下参数predicate,这个参数是用来筛选哪些类是Controller,这个方法的实现也比较有意思:

 internal static bool   IsControllerType(  Type   t) {
              return
                  t !=   null   &&
                t.IsPublic &&
                t.Name.EndsWith(  "Controller"  ,   StringComparison  .OrdinalIgnoreCase) &&
                !t.IsAbstract &&
                  typeof  (  IController  ).IsAssignableFrom(t);
        } 

这就说明一个类如果要能够成为Controller, 必须以Controller结尾,必须是public的,必须实现IController接口。找到所有的Controller类之后,再回到EnsureInitialized方法中,为了方便查找,会对这些类进行索引,首先按照Controller的名字进行分组,然后再按照Namespace分组。下面很快可以看到,这样分组之后可以很方便的找到需要的Controller。

回到  GetControllerTypeWithinNamespaces  方法,这时候缓存中已经有索引好的Controller的信息了,接着就是在缓存中根据Controller的名字查找Controller,GetControllerTypes实现了这个过程,过程并不复杂,但是细节不少,具体代码不贴出,过程是:首先查出controller名字对应的Lookup,再检查namespace是否符合。如果 GetControllerTypeWithinNamespaces中的参数namespace为空或者没有内容,那么就只判断controller的名字。查找的结果有三种,只有一个Controller的type满足,那么就返回这个类型,如果没有找到则返回null,如果找到了多个就会抛出异常。

回到GetControllerType方法,如果 GetControllerTypeWithinNamespaces  返回了null,并且UseNamespaceFallback 设为true,那么会进行下一步的搜索,否则返回null。下一步的搜索就是在项目的DefaultNamespace下进行搜索,对于没有Namespace的RouteData,默认就是在这里搜索的。最后在所有的namespace中搜索。至此,根据controller名字查找controller类的type完成了。

接下来就要实例化这个类型。实例化的方法简单些只需要调用

 Activator  .CreateInstance(controllerType); 

就可以了。但是ASP.NET MVC在这里使用了较为复杂的DI机制,在默认情况下,它调用的是DefaultDependencyResolver的GetService方法,这个方法最终也仅仅是调用了Activator.CreateInstance方法。关于MVC中的DI机制,这里不多做分析,另文叙述。 至此,一个Controller类已经被构造出来了。下文介绍Action的激活。

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Controller的激活的详细内容...

  阅读:41次