CommunityServer如何体现“重配置、轻编程”Config实现
.net同java2以后的编程一样,开始强调“重配置、轻编程”了,所以出现了.config类别的扩展名。通常,我们的配置信息不多,所以会写入到web.config上,但是CS的配置信息比较多,所以单独设定了一个 communityserver.config 文件来存储和配置系统的设定信息。
解析该config文件的类是
CSConfiguration.cs
该类是整站各模块部分用来实现系统灵活性的基础工具类,这样需要确保协同各模块必须保持配置的一致性,这样,自然选择了单例模式来实现类。第二个需要学习的是,既然配置信息如此重要,会影响性能,所以配置类被进行了缓存。看看如何进行单例实例化的:
public
static CSConfiguration GetConfig()
{
CSConfiguration config = CSCache.Get(CacheKey) as CSConfiguration;
if(config == null)
{
string path = null;
HttpContext context = HttpContext.Current;
if(context != null)
path = context.Server.MapPath("~/communityserver.config");
else
path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "communityserver.config");
XmlDocument doc = new XmlDocument();
doc.Load(path);
config = new CSConfiguration(doc);
CSCache.Max(CacheKey,config,new CacheDependency(path));
CSCache.ReSetFactor(config.CacheFactor);
//CSEvents.PostConfigurationInitialized(config);
}
return config;
}
首先,检查cache中是否存在已经实例化,如果实例化存在 就直接返回;如果不存在,就开始进行实例化过程。
当然,具体的实例化采用携带XmlDocument实例的构造函数来,你也可以通过public构造函数来获取实例,也就是可以new 但是,看该类的代码,应该是通过
GetConfig来获取比较合适了。
进入构造函数,就开始读取XML进入config的实例内部成员设定了,根据遇到的节点不同,分别调用相应的处理函数,在具体的针对各节的处理函数里面来按照XML节点的格式来读取设定配置。如果要写关于配置文件的设定帮助文档的话,那么就需要查看源代码的实现细节了。
通常情况下,如果是收集,就用Hashtable来存储,少量采用数组。
除了CSConfig类外,各个组件工程也都有自己的配置处理对象。逐一来看看。
在CommunityServer.config中 有关于各个组件的配置节:
<
Gallery
allowEncodedUnicodeCharsInMetadata
="true"
>
<AttachmentSettings
enableFileSystemStorage
="true"
fileSystemStorageLocation
="~/photos/storage"
enableDataStoreStorage
="true"
enableDirectLinks
="false"
extensions
=
"gif,jpg,jpeg,png,bmp,GIF,JPEG,JPG,PNG,BMP,Gif,Jpg,Jpeg,Png,Bmp"
/>
<CacheSettings
enableFileSystemStorage
="true"
fileSystemStorageLocation
="~/photos/cache"
enableDataStoreStorage
="false"
enableDirectLinks
="false"
/>
</Gallery>
该节对应Gallery的配置处理。
<Weblog
defaultTheme
=
"default"
enableSkinCache
=
"true"
enableThemes
=
"true"
aggregatePostSize
=
"250"
createDirectories
=
"true"
enableCommentRedirects
=
"true"
servicePostCountLimit
=
"25"
aggregatePostCount
=
"25"
individualPostCount
=
"15"
authenticateOnSectionNotFound
="false"
>
<AttachmentSettings
enableFileSystemStorage
="true"
fileSystemStorageLocation
="~/blogs/files"
enableDataStoreStorage
="true"
enableDirectLinks
="false"
extensions
=
"zip,xml,txt,gif,jpg,jpeg,png,doc,xls,mp3,mmv"
/>
<DefaultPingServices>
<addurl="http://rpc.weblogs.com/RPC2"/>
<addurl="http://ping.blo.gs/"/>
<addurl="http://rpc.technorati.com/rpc/ping"/>
<addurl="http://xping.pubsub.com/ping"/>
</DefaultPingServices>
</Weblog>
该节对应
Weblog的配置处理
<Forums>
<AttachmentSettings
enableFileSystemStorage
="true"
fileSystemStorageLocation
="~/forums/storage"
enableDataStoreStorage
="true"
enableDirectLinks
="true"
/>
</Forums>
该节对应
Forums的配置处理
<FileGalleries>
<AttachmentSettings
enableFileSystemStorage
="true"
fileSystemStorageLocation
="~/files/storage"
enableDataStoreStorage
="true"
enableDirectLinks
="false"
extensions
=
"all"
/>
</FileGalleries>
该节对应
文件共享文件夹的配置处理
<Adsroles="SystemAdministrator"enable="false"/>
该节对应
系统广告块的配置处理
<SpamautoModerateScore="5"autoDeleteScore="10"></Spam>
该节对应
系统插件块的配置处理
<ReaderLastModifiedInterval="60"
roles
=
"SystemAdministrator;FeedReaderAdministrator"
>
<BlogRollerTruncationLength="500"/>
</Reader>
该节对应
在线阅读的配置处理
CommunityServer.Components. ApplicationConfiguration是全部组件的配置文件的基类。详细在注释中理解吧
[Serializable]
public abstract class ApplicationConfiguration
{
public ApplicationConfiguration(){}
//保护的静态 instance方法,显然想单例模式吧
protected static ApplicationConfiguration Instance(Type type, bool useCache)
{
ApplicationConfiguration config = null;//实力本身
if(useCache)
{//这个Type应该是代表当前的 ApplicationConfiguration 的某个子类的类型,如果使用了Cache就从CSCache中获取该配置类的子类实例
config = CSCache.Get(CacheKey(type)) as ApplicationConfiguration;
}
if(config == null)
{ //如果实例null那么通过反射 来创建实例,并获得该类的基类
config = Activator.CreateInstance(type) as ApplicationConfiguration;
if(config != null)
{//如果实例化成功,那么进行子类的个性化处理,也就是执行CSConfiguration.GetConfig().GetConfigSection config.ConfigSection代表子类对应于CommunityServer.config的相应配置节
XmlNode node = CSConfiguration.GetConfig().GetConfigSection(config.ConfigSection);
config.BaseSettings(node); //调用基础设定,也就是 ApplicationConfiguration 类的属性设定,在此方法中处理节点的设定
config.Initialize(node);
if(!config.FileOnly) //此处语义应当是指定,当不是仅仅从配置文件获取配置信息(那说明有的配置文件是在文件意外的存储方式内获取的,我想有些blog设定配置信息应该是存储在数据库中的吧)
{//不错,看来这里是进行DataProvider的获取,显然是从数据存储处获取配置的处理细节了
CommonDataProvider dp = CommonDataProvider.Instance();
object config2 = dp.GetApplicationConfigurationSettings(config.ApplicationType,type);
Merger.Merge(config,config2); //合并细节待后
}
if(useCache)
{
CacheConfigSettings(config); //如果成功获得实例咱们就Cache之
}
}
CSEvents.PostConfigurationInitialized(config); //记录下初始化事件,应该是为了调试系统而设定的吧
}
return config;
}
private static string CacheKey(Type type) //唯一的明确的缓存键
{
return string.Format("AppConfig-{0}-{1}", type.FullName,CSContext.Current.SettingsID);
}
private static void CacheConfigSettings(ApplicationConfiguration appConfig)
{//此处是缓存当前的配置对象 参考下面的cache对象分析
appConfig._isCached = true;
CacheDependency dep = new CacheDependency(null, new string[]{CSConfiguration.CacheKey});
CSCache.Remove(CacheKey(appConfig.GetType()));
CSCache.Insert(CacheKey(appConfig.GetType()), appConfig, dep,CSCache.MinuteFactor*15);
}
///<summary>
/// removes the specified application configuration from the cache if it exists;
///</summary>
///<param name="type"></param>
protected static void Flush(Type type) //刷新缓存就是将缓存去掉,这样会重新装载缓存的
{
CSCache.Remove(CacheKey(type));
}
protected abstract string ConfigSection { get;}
public abstract ApplicationType ApplicationType { get;}
private bool _enabled = true;
private bool _isLocked = false;
private bool _allowNew = true;
private bool _disabled = false;
private bool _fileOnly = false;
private bool _isCached = false;
public bool Enabled
{
get{return _enabled;}
set{_enabled = value;}
}
[ReadFromConfigurationFileOnly]
public bool IsLocked
{
get{return _isLocked;}
}
[ReadFromConfigurationFileOnly]
public bool IsCached
{
get{return _isCached;}
}
[ReadFromConfigurationFileOnly]
public bool FileOnly
{
get{return _fileOnly;}
}
[ReadFromConfigurationFileOnly]
public bool AllowNew
{
get{return _allowNew;}
}
[ReadFromConfigurationFileOnly]
public bool Disabled
{
get{return _disabled;}
}
protected virtual void Initialize(XmlNode node)
{
}
protected void BaseSettings(XmlNode node)
{
if(node != null)
{
XmlAttribute fo = node.Attributes["fileOnly"];
if(fo != null)
this._fileOnly = bool.Parse(fo.Value);
XmlAttribute att = node.Attributes["enable"];
if(att != null)
this.Enabled = bool.Parse(att.Value);
XmlAttribute loc = node.Attributes["locked"];
if(loc != null)
this._isLocked = bool.Parse(loc.Value);
XmlAttribute dis = node.Attributes["disabled"];
if(dis != null)
this._disabled = bool.Parse(dis.Value);
XmlAttribute allownew = node.Attributes["allowNew"];
if(allownew != null)
this._allowNew = bool.Parse(allownew.Value);
}
}
public virtual void Save() //这个只支持非文件存储的配置信息,具体是通过数据存取Provider来获取
{
if(!this.FileOnly)
{
CommonDataProvider dp = CommonDataProvider.Instance();
dp.CreateUpdateApplicationConfigurationSettings(this.ApplicationType, this);
//Update this copy to the cache if we are not already cached. This will not help
//us in a cluster
if(!this.IsCached)
{
CacheConfigSettings(this);
}
}
}
}
以上的类中的字段、方法设定ReadFromConfigurationFileOnly属性的表示改元素只支持从文件获取配置信息时候的字段有效映射。
ApplicationConfiguration 是一个
abstract 类,而为了满足论坛和博客应用,衍生了
ApplicationKeyApplicationConfiguration 和
ThemedApplicationKeyApplicationConfiguration
譬如,FileGarry是 ApplicationKeyApplicationConfiguration 而 blog是 ThemedApplicationKeyApplicationConfiguration。
下面来逐个具体的实例分析
WeblogConfiguration
该类实际上继承于 ThemedApplicationKeyApplicationConfiguration 看名字知道,这个配置是支持Theme的ApplicationKeyApplicationConfiguration,同时 ThemedApplicationKeyApplicationConfiguration 又派生自 ApplicationKeyApplicationConfiguration ,ApplicationKeyApplicationConfiguration是需要主键Key的配置文件,ApplicationKeyApplicationConfiguration最终派生自 ApplicationConfiguration,即:
WeblogConfiguration –》ThemedApplicationKeyApplicationConfiguration –》ApplicationKeyApplicationConfiguration—》ApplicationConfiguration
我们重点分析下其 构造函数和Instance方法:
public
static WeblogConfiguration Instance()
{
return Instance(true); //true表示需要cache
}
public static WeblogConfiguration Instance(bool useCache)
{
return Instance(thisType, useCache) as WeblogConfiguration; //此时调用的是最上层基类 ApplicationConfiguration 的Instance方法
}
该类重写的部分有:
#region
Overridden Base Class Members
protected override string ConfigSection
{
get { return "CommunityServer/Weblog"; }
}
public override ApplicationType ApplicationType
{
get { return ApplicationType.Weblog; }
}
#endregion
#region
Initialize
protected override void Initialize(XmlNode node)
{
base.Initialize(node); //父类的Initialize
if(node != null)
{
……
//为 WeblogConfiguration 的个性化设置部分
}
}
#endregion
和
protected override string ThemeLocationDefault() { return "~/Themes/Blogs/"; }
这些覆盖的部分,通过其属性被逻辑层读取并进行运算。
FileGalleryConfiguration
继承结构
FileGalleryConfiguration
–
》ApplicationKeyApplicationConfiguration
–
》ApplicationConfiguration
ForumConfiguration
ForumConfiguration
–
》ApplicationConfiguration
其Instance 函数:
public
static ForumConfiguration Instance()
{
return Instance(true);
}
public static ForumConfiguration Instance(bool useCache)
{
return Instance(thisType, useCache) as ForumConfiguration;
}
同样通过最上的基类
ApplicationConfiguration 来实例化并缓存自己。Initialize函数加入自身的个性化的XML文件处理代码。
其他类都类似:
1、 通过
ApplicationConfiguration 的Instance函数来获得单例实例化功能
2、
通过 ApplicationConfiguration就已经定义的 Initialize函数的覆盖来实现自身的个性化数据处理。
3、 针对不同的Config的存储形式(文件或者DB),在 ApplicationConfiguration 就进行了区分,并通过DataProvider来获得具体配置信息或者XML的Node。
4、通过属性来提供配置信息的存取界面
在建立了系统的Config实例后,各个组件部分会依据自身的设计,主动读取配置信息,从而决定自身的执行流程,当然需要考虑各类不同的情况,配置情况来实现所谓的“重配置”。
阅读此类时候,将实体 Provider 类放在 该类我觉得有点不大妥当,既然都那么多文件了,也不怕多一个Provider.cs文件吧。另外,如果在定义private等一大堆成员变量的时候,能够用region来做下说明归类,我觉得可能更利于大家阅读。
这个是属于CS整体框架的基础类,所以大家需要了解其具体细节。
分析此类,我们也顺带阅读下CSCache类,这个集成系统提供的Cache功能的类。
由于
System.Web.Caching 的Cache类是系 统提供的,而且无法继承,所以采用集成手段来OOP系统的Cache功能,也显然这样的一个类只能是类似CSConfig那样采用单例模式来实现。该Cache 有一个private 的空构造函数和静态构造函数。静态的构造函数,从HttpContext类或者
HttpRuntime中获得Cache功能。
static
CSCache()
{
HttpContext context = HttpContext.Current;
if(context != null)
{
_cache = context.Cache;
}
else
{
_cache = HttpRuntime.Cache;
}
}
与CSCache类似,还有一个CSContext类也是属于集成系统框架的特定类,我们可对比CSCache来看看。通常,你如果使用系统提供的某类并集成的话,你都会或多或少要遵守该类的一些诸如实例生成啊等的约定。