代码改变世界

我记录开源系统1.6源码解析(二)上

2012-01-12 23:09  爱研究源码的javaer  阅读(324)  评论(0编辑  收藏  举报

今天我们来解析

wojilu.Web.Mvc.MvcFilterLoader.Init();

这行很重要的代码,我记录已经提示说是mvc过滤器的加载器, 顾名思义,肯定和我记录的mvc模式相关。

首先我们按VS F12快捷键转入MvcFilterLoader类,我们可以发现该类只有一个静态方法Init():

public static void Init() {

            List<String> filter = MvcConfig.Instance.Filter;//获取filter : wojilu.Web.Controller.RenderHelper

            foreach (String t in filter) {

                IMvcFilter f = ObjectContext.GetByType( t ) as IMvcFilter;
                if (f == null) continue;
                f.Process( MvcEventPublisher.Instance );

            }

        }
首先定义List类型的filter变量,该变量通过MvcConfig类实例的Filter成员获得。MvcConfig是关于Mvc配置的类:
public static readonly MvcConfig Instance = new MvcConfig();
/// <summary>
      /// 自定义的过滤器类型,比如 wojilu.Web.Controller.RenderHelper
      /// </summary>
      public List<String> Filter { get { return _filterList; } }
MvcConfig包含一个List<String>类型的Filter属性,返回_filterList这个私有成员,我们再来看看MvcConfig的构造函数:
private MvcConfig() {

          Dictionary<String, String> dic = cfgHelper.Read( PathHelper.Map( strUtil.Join( cfgHelper.ConfigRoot, "mvc.config" ) ) );

          _routeConfigPath = PathHelper.Map( strUtil.Join( cfgHelper.ConfigRoot, "route.config" ) );

          _rootNamespace = getRootNamespace(dic);//// 多个 namespace 之间用英文逗号隔开rootNamespace: wojilu.Web.Controller
          _isParseAppId = getIsParseAppId( dic );
          _isCacheView = getIsCacheView( dic );

          _urlExt = getUrlExt( dic );
          _viewExt = getViewExt( dic );
          _viewDir = getViewDir( dic );
          _filterList = getFilterList( dic );

          dic.TryGetValue( "jsVersion", out _jsVersion );
          dic.TryGetValue( "cssVersion", out _cssVersion );
          dic.TryGetValue( "staticDomain", out _staticDomain );
          dic.TryGetValue( "noLogError", out _noLogError );
      }
首先定义一个读取mvc.config配置文件的字典集合,我们可以先忽略其他代码直接看_filterList,它通过getFilterList这个方法来赋值:
private List<String> getFilterList( Dictionary<String, String> dic ) {

           List<String> result = new List<String>();

           String filter;
           dic.TryGetValue( "filter", out filter );
           if (strUtil.IsNullOrEmpty( filter )) return result;

           String[] arrF = filter.Split( ',' );
           foreach (String f in arrF) {
               if (strUtil.IsNullOrEmpty( f )) continue;
               result.Add( f.Trim() );
           }

           return result;
       }

可以看到关键dic.TryGetValue(“filter”,out filter)这行代码,从集合里取出关键字为filter的值,返回result.也就是说我们

mvc.config配置文件里需包含以filter开头的键值对。

       我们再回到MvcFilterLoader的Init方法中来,获取到filter集合后,就遍历该集合

foreach (String t in filter) {

                IMvcFilter f = ObjectContext.GetByType( t ) as IMvcFilter;
                if (f == null) continue;
                f.Process( MvcEventPublisher.Instance );

            }
这里对集合中的每一项做了处理,这里引入了IOC管理容器ObjectContext,我们直接看它的GetByType方法
/// <summary>
        /// 从缓存中取对象(有注入的就注入,没有注入的直接生成),结果是单例
        /// </summary>
        /// <param name="typeFullName"></param>
        /// <returns></returns>
        public static Object GetByType( String typeFullName ) {
            if (Instance.TypeList.ContainsKey( typeFullName ) == false) return null;
            Type t = Instance.TypeList[typeFullName];
            return GetByType( t );
        }
首先判断ObjectContext单例Instance中是否包含该filter,(注:以下代码过于深入,可以忽略)单例定义如下:
/// <summary>
        /// 容器的实例(单例)
        /// </summary>
        public static ObjectContext Instance {
            get {
                if (_instance == null) {
                    lock (syncRoot) {
                        if (_instance == null) {
                            ObjectContext ctx = new ObjectContext();
                            InitInject( ctx );
                            _instance = ctx;
                        }
                    }
                }
                return _instance;
            }
        }

首先判断是否已存在_Instance,不存在就给他赋值,_instance定义如下:

private static volatile ObjectContext _instance;

volatile这个关键字可以参考http://msdn.microsoft.com/zh-cn/x13ttww7.aspx

这里最重要的就是InitInject方法了

private static void InitInject( ObjectContext ctx ) { loadAssemblyAndTypes( ctx ); resolveAndInject( ctx ); addNamedObjects( ctx ); }

连续调用了三个函数:loadAssemblyAndTypes,resolveAndInject,addNameObjects:

private static void loadAssemblyAndTypes( ObjectContext ctx ) {

           String appSettings = cfgHelper.GetAppSettings( "InjectAssembly" );
           if (strUtil.IsNullOrEmpty( appSettings )) return;

           String[] strArray = appSettings.Split( new char[] { ',' } );
           foreach (String asmStr in strArray) {

               if (strUtil.IsNullOrEmpty( asmStr )) continue;
               String asmName = asmStr.Trim();
               Assembly assembly = loadAssemblyPrivate( asmName, ctx );
               findTypesPrivate( assembly, asmName, ctx );
           }
       }
首先从web.config中获取InjectAssemblys,然后遍历该集合,loadAssemblyPrivate函数如下
private static Assembly loadAssemblyPrivate( String asmName, ObjectContext ctx ) {
         Assembly assembly = Assembly.Load( asmName );
         ctx.AssemblyList.Add( asmName, assembly );
         return assembly;
     }

其实绕了这么多最终是在ObjectContext中添加AssemblyList。findTypesPrivate函数如下:

private static void findTypesPrivate( Assembly assembly, String asmName, ObjectContext ctx ) {
           Type[] types = assembly.GetTypes();
           ctx.AssemblyTypes.Add( asmName, types );
           
           foreach (Type type in types) {
               ctx.TypeList.Add(type.FullName, type);//这里加入InjectAssembly下的所有程序集
           }
       }

也是对ObjectContext中的AssemblyTypes,TypeList集合添加项。

loadAssemblyAndTypes就做这么多工作.接下来我们看resolveAndInject函数:

private static void resolveAndInject( ObjectContext ctx ) {
           List<MapItem> maps = cdb.findAll<MapItem>();
           if (maps.Count <= 0) return;

           Dictionary<String, MapItem> resolvedMap = new Dictionary<String, MapItem>();

           logger.Info( "resolve item begin..." );
           resolveMapItem( maps, resolvedMap, ctx );

           logger.Info( "inject Object begin..." );
           injectObjects( maps, resolvedMap );

           ctx.ResolvedMap = resolvedMap;
       }
这里首先查找List<MapItem>类型的maps,用到了cdb这个类:
/// <summary>
  /// 从内存数据库中查询数据
  /// </summary>
  /// <remarks>
  /// 数据持久化在 /framework/data/ 目录下,以json格式存储。加载之后常驻内存。
  /// 特点:直接从内存中检索,速度相当于 Hashtable。插入和更新较慢(相对而言),因为插入和更新会在内存中重建索引。
  /// </remarks>
  public class cdb 
很清楚的可以看到此类的作用,MapItem也是比较重要的一个类
/// <summary>
    /// 依赖注入中的配置项
    /// </summary>
    public class MapItem : CacheObject
此类继承自CacheObject
/// <summary>
   /// 缓存对象,常驻内存,同时以json格式存储在磁盘中
   /// </summary>
   [Serializable]
   public class CacheObject 
这两个类留待今后的详细解析。。我们再回到cdb.findAll<MapItem>()这个方法上,看看cdb的fingAll泛型方法:
/// <summary>
/// 查询类型 T 的所有数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>返回所有数据的列表</returns>
public static List<T> findAll<T>() where T : CacheObject {
    IList list = MemoryDB.FindAll( typeof( T ) );
    return db.getResults<T>( list );
}
泛型方法再加上T类型的约束CacheObject,我们又看到了两个新类MemoryDB和db类,MemoryDB的FindAll方法如下:
internal static IList FindAll( Type t ) {
            return new ArrayList( GetObjectsByName( t ) );
        }
这里采用internal关键字了。。返回一个ArrayList,GetOejectsByName方法如下:
private static IList GetObjectsByName( Type t ) {

            if (isCheckFileDB( t )) {

                lock (chkLock) {

                    if (isCheckFileDB( t )) {

                        loadDataFromFile( t );
                        _hasCheckedFileDB[t] = true;

                    }

                }

            }
            return (objectList[t.FullName] as IList);
        }
这里调用isCheckFileDB,loadDataFromFile,不管怎样都是要返回objectList[t.FullName],看看objectList集合
private static IDictionary objectList = Hashtable.Synchronized( new Hashtable() );

还是个IDictionary类型..回过来再看isCheckFileDB函数:

private static Boolean isCheckFileDB( Type t ) {
            if (_hasCheckedFileDB[t] == null) return true;
            return false;
        }
如果_hasCheckedFileDB中不包含t,就返回true.
private static Hashtable _hasCheckedFileDB = new Hashtable();
再看看loadDataFromFile函数:
private static void loadDataFromFile( Type t ) {
            if (wojilu.IO.File.Exists( getCachePath( t ) )) {
                IList list = getListWithIndex( wojilu.IO.File.Read( getCachePath( t ) ), t );
                objectList[t.FullName] = list;
            }
            else {
                objectList[t.FullName] = new ArrayList();
            }
        }

其实isCheckFileDB函数和loadDataFromFile函数都是为了返回objectList[t.FullName]..

 

可以看见,我记录中的代码是一层套一层,很容易被搞晕,也可以看出作者付出了很多劳动。其实分析了这么多也就是在

resolveAndInject函数中。。

调用

List<MapItem> maps = cdb.findAll<MapItem>();
           if (maps.Count <= 0) return;

最终目的是在

logger.Info( "resolve item begin..." );
resolveMapItem( maps, resolvedMap, ctx );

logger.Info( "inject Object begin..." );
injectObjects( maps, resolvedMap );

ctx.ResolvedMap = resolvedMap;

这几句代码上,详细代码就不一一展开。。其实就是把系统操作记录日志,DI操作等。

我们可以简单的归纳为ObjectContext.GetByType(String typeFullName) -->判断ObjectContext单例Instance中是否包含该filter,有就从Instance.TypeList里去取,最后就通过GetByType函数返回单例:

/// <summary>
       /// 从缓存中取对象(有注入的就注入,没有注入的直接生成),结果是单例
       /// </summary>
       /// <param name="t"></param>
       /// <returns></returns>
       public static Object GetByType( Type t ) {
           if (t == null) return null;
           Object result = Instance.ObjectsByType[t.FullName];

           if (result == null) {

               MapItem mapItem = getMapItemByType( t );
               if (mapItem != null) {
                   return createInstanceAndInject( mapItem );
               }
               else {
                   return rft.GetInstance( t );
               }

           }
           else
               return result;
       }

我记录的DI和Data这块今后需要深入解析下。。

再说的简单点,MvcFilterLoader.Init()函数中的

IMvcFilter f = ObjectContext.GetByType( t ) as IMvcFilter;
这行就是获取
/// <summary>
   /// MvcFilterLoader中初始化调用的
   /// </summary>
   public class RenderHelper : IMvcFilter 
该类在wojilu.Web.Controller命名空间下。关于它的Process函数
public void Process( MvcEventPublisher publisher ) {

            publisher.Begin_ProcessMvc += new EventHandler<MvcEventArgs>( publisher_Begin_ProcessMvc );
            publisher.Begin_Render += new EventHandler<MvcEventArgs>( publisher_Begin_Render );

        }
我们将在下篇文章中详细解析。。