今天我们来解析
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 ); }我们将在下篇文章中详细解析。。