CSModule实现的事件监听体系
CS里面的CSModule是个什么东东?
我们知道,系统大了,必然是比较复杂,系统内部很多事件需要被各个相互联系的模块相互截取,相互了解。我们要针对CS系统做出开发,实际上我们很大程度上是针对CS内部流程执行过程中的大量事件进行面向事件的处理代码编写。譬如,我们要做到系统发生了例外,我们可以敏感接获到,并且需要在特定的“安全区域”进行处理例外;再比如,我们有时候会针对某个帖子进行监控,如果发生了新帖需要被通知,如果硬编码进入当然可以,但是假若需要接获新帖通知的不止一个相关体系,那么硬编码显然不符合要求,这样,就需要一种协调机制,引入一种消息通告模型,让内部事件可以通过这种机制安全有序转入到关心事件的处理部分,这种机制在CS中就是通过CSModule来实现的。
CSEvent 定义了系统内大部分事件。包含了Users、Posts、Sections、Groups、PostCategories、Ratings、Favorites、Exceptions、Search、UserInvitations等对象的事件。但是每一个事件都是调用了
CSApplication实例的相应事件函数。如关于Search的事件:
public static void PreSearch(SearchQuery query)
{
CSApplication.Instance().ExecutePreSearch(query);
}
public static void PostSearch(SearchResultSet results)
{
CSApplication.Instance().ExecutePostSearch(results);
}
这些事件需要在特定代码流中被触发,而这些事件会转入到 CSApplication的实例中。
CSApplication是单例模式,并且通过CSCache缓存实例。其Instance函数:
internal
static CSApplication Instance()
{
const string key = "CSApplication";
CSApplication app = CSCache.Get(key) as CSApplication;
if(app == null)
{
lock(sync)
{
app = CSCache.Get(key) as CSApplication;
if(app == null)
{
CSConfiguration config = CSContext.Current.Config;
XmlNode node = config.GetConfigSection("CommunityServer/CSModules");
app = new CSApplication();
if(node != null)
{
foreach(XmlNode n in node.ChildNodes)
{
if(n.NodeType != XmlNodeType.Comment)
{
switch(n.Name)
{
case "clear":
app.modules.Clear();
break;
case "remove":
XmlAttribute removeNameAtt = n.Attributes["name"];
string removeName = removeNameAtt == null ? null : removeNameAtt.Value;
if(!Globals.IsNullorEmpty(removeName) && app.modules.ContainsKey(removeName))
{
app.modules.Remove(removeName);
}
break;
case "add":
XmlAttribute en = n.Attributes["enabled"];
if(en != null && en.Value == "false")
continue;
XmlAttribute nameAtt = n.Attributes["name"];
XmlAttribute typeAtt = n.Attributes["type"];
string name = nameAtt == null ? null : nameAtt.Value;
string itype = typeAtt == null ? null : typeAtt.Value;
if(Globals.IsNullorEmpty(name))
{
EventLogs.Warn(string.Format("A CSModule could not be loaded. The name was not defined. Type {0}", itype), "CSModules", 654, CSContext.Current.SettingsID);
continue;
}
if(Globals.IsNullorEmpty(itype))
{
EventLogs.Warn(string.Format("A CSModule ({0}) could not be loaded. No type was defined", name), "CSModules", 655, CSContext.Current.SettingsID);
continue;
}
Type type = Type.GetType(itype);
if(type == null)
{
EventLogs.Warn(string.Format("A CSModule ({0}) could not be loaded. The type {1} does not exist", name,itype), "CSModules", 656, CSContext.Current.SettingsID);
continue;
}
ICSModule mod = Activator.CreateInstance(type) as ICSModule;
if(mod == null)
{
EventLogs.Warn(string.Format("A CSModule ({0}) could not be loaded. The type {1} could not be instantiated", name,itype), "CSModules", 657, CSContext.Current.SettingsID);
continue;
}
mod.Init(app, n);
app.modules.Add(name,mod);
break;
}
}
}
}
CacheDependency dep = new CacheDependency(null, new string[]{CSConfiguration.CacheKey});
CSCache.Max(key, app,dep);
}
}
}
return app;
}
以上代码通过分析
"CommunityServer/CSModules"来加载需要监听事件的模块(CSModule)
ICSModule mod = Activator.CreateInstance(type) as ICSModule;
这里实例化ICSModule利用的是反射。通过此语句 还初始化模块,参与监听时间。监听事件 采用的是Add remove形式,允许事件广播机制
在获得CSModule的实例后,马上调用了其Init函数,这类函数主要是携带一个当前CSApplication的实例,并携带
CommunityServer/CSModules配置当前节点的XML节点对象。通常的Inti函数应当通过如下类似代码进行事件的挂接:
public
void Init(CSApplication csa, XmlNode node)
{
csa.UserKnown +=new CSUserEventHandler(csa_UserKnown);
}
也就是通知CSApplication,该模块会接获其中的事件。
那么再回到CSApplication 如何管理这些事件并进行分发的呢?
在Init函数内部使用 += 来监听事件的时候,此时会将监听回调函数存储:
public
event CSUserEventHandler UserKnown
{
add{Events.AddHandler(EventUserKnown, value);}
remove{Events.RemoveHandler(EventUserKnown, value);}
}
此时通过add remove 将回调函数句柄存储到哈希表中,这样就允许一个事件多个回调函数。
这样,大家可看到在CS中,事件流是通过
CSApplication实例来汇聚,通过其中的事件机制分发到在Init函数中注册的多个Module,这样的分发路径就是:
事件源 ---》CSApplication实例 –》 CSModule1
|
------------》CSModule2
|-----》CSModule3 。。。。。
这样设计的好处呢?
1、 事件的产生与事件的处理是分离的。
2、 可以做到事件的广播,多个监听器都可以处理事件;而且这种处理可以通过配置文件来实现。做到,“轻编程重配置”,还做到松耦合。
新增一个事件,有一定的布局结构参考。有利于系统的扩展扩充。