.NET配置文件解析过程详解
2008-06-30 21:20
1116 查看
在我看来,WEB project的开发与WINFORM的开发最大的区别在于web的运行是在Framework上更高一层框架上运行,即ASP。NET框架,程序员在web下的开发可以说是黑盒开发,不是让你去定义程序入口和执行顺序,而是asp.net来调用你的各个方法,程序员做的一切都是一种受控的舞蹈。就像我们调用nunit之类的工具来测试一个dll一样,nunit是容器,是框架,执行哪个方法是由nunt来决定的。因此,也就有了页面执行周期各状态等令刚入门的程序员困惑不已的事,其实,究其根源,在于不了解容器而去使用容器。对于asp.net框架的学习,我们不妨从配置文件开始。
对于程序开发者而言,写配置文件是经常性的工作,如果你写了一个xx.config文件,如果没有详尽的注释,别人恐怕很难读懂,没有良好的配置架构,程序也失去了活力。在我看来,.net配置文件的特点在于反射定义和继承性。
我们访问配置文件时经常觉得配置文件的结构不太符合我们的需要,我们需要从里面更方便地获得自己定义的对象,而不仅仅是key和value,对于自定义配置文件的著述已有很多,在此不再描述,有兴趣的朋友可以访问http://ly4cn.cnblogs.com/archive/2005/09/06/231245.html。
自定义配置节其实还是在.net配置文件架构的应用而已,我们先来搞懂配置文件的结构,弄清楚.net配置文件的运行方式。下面是machine.config的一部分内容:
<configSections>
<section name="runtime" type="System.Configuration.IgnoreSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowLocation="false" />
<sectionGroup name="system.net">
<section name="authenticationModules" type="System.Net.Configuration.NetAuthenticationModuleHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</ sectionGroup>
</configSections>
SDK中<section>的定义为:
<section
name="section name"
type="configuration section handler class, assembly"
allowDefinition="Everywhere|MachineOnly|MachineToApplication"
allowLocation="true|false" />
<sectionGroup>的定义为:
<sectionGroup
name="section group name"/>
</sectionGroup>
我们来看看.net框架内是如何利用这种结构的。反编译System.dll找到GetConfig方法,在里面我们发现实际获取config的工作默认是由实现了IConfigurationSystem的DefaultConfiguationSystem类来实现的。
public static object GetConfig(string sectionName)
我们再来看DefaultConfigurationSystem,这个类主要包含了machine.config的名称路径的基本信息和一些uri操作,而实际的GetConfig的操作交给了ConfiguationRecord来处理,这个类没有实现任何接口,可见他和DefaultConfiguration是绑定在一起的。
1internal class DefaultConfigurationSystem : IConfigurationSystem
2
事实上所有的配置文件的分析和获取都是在ConfiguationRecord里实现的,作为配置文件分析的第一步,正如我们经常做的一样->加载一个配置文件,这个方法公开为 Load(filename)。DefaultConfiguationSystem的Init()方法中用machine.config创建了一个ConfiguationRecord对象,并将其作为父对象传递给当前程序的ConfiguationRecord对象,当然前提是当前程序有配置文件,比如myapp.config,然后再加载当前程序的配置文件,从而实现配置文件的继承。
void IConfigurationSystem.Init()
现在我们可以专注于ConfiguationRecord的具体实现了,Load方法中得到一个XmlTextWriter,并执行.ScanFactoriesRecursive和ScanSectionsRecursive方法。
reader1 = ConfigurationRecord.OpenXmlTextReader(filename);
if (reader1 != null)
ScanFactoriesRecursive方法会调用他的一个重载方法来解析<configSections>中的<sectionGroup>,<section>,<remove>,<clear>,我们写配置文件时大小写可不能写错哦,.NET没有做toslower之类的转换,直接就是 “== “。在这个方法里程序会将解析得到的sectiongroup以key=tagkey,value= ConfigurationRecord.GroupSingleton的方式存到EnsureFactories里,将section以key=tagkey,value=typestring的方式储存,值得注意的是,这里并没有创建实现IConfigurationSectionHandler的实例对象,而是将其类型名(比如:字符串”system.Configuration.NameValueSectionHandler”)作为值到EnsureFactories,等到后面GetConfig的时候再来反射创建。<remove>则存为ConfigurationRecord.RemovedFactorySingleton。<clear>就清空EnsureFactories。这里的tagkey是各级name的组合,比如”mygroup/mysection”这样以分隔符”/”组合的形式。应该客观地说这部分代码用了很多goto语句,可读性不是太好,但这样写也确实没有什么问题。
this.CheckRequiredAttribute(text3, "name", reader);
this.CheckRequiredAttribute(text4, "type", reader);
this.VerifySectionName(text3, reader);
string text5 = ConfigurationRecord.TagKey(configKey, text3);
if (this.HaveFactory(text5) != ConfigurationRecord.HaveFactoryEnum.NotFound)
this.EnsureFactories[text5] = text4;
goto Label_02B6;
ScanSectionsRecursive方法会解析配置文件里的section实例,并将其tagkey储存到Hashtable _unevaluatedSections中,表示尚未evaluated的configkey的集合,可见section实例对象的创建和handler一样,都是fetch when need。在后面的操作Getconfig中会使用他。
if (this._unevaluatedSections == null)
this._unevaluatedSections[text2] = null;
现在我们就可以看Getconfig方法是怎么来执行得到我们想要的对象的。
public object GetConfig(string configKey)
如果_result中有对应configkey的section实例,就返回,没有则需要对configkey进行ResolveConfig,将解析到的对象保存到_result中并返回给用户。在ResolveConfig方法中,可以看到如果当前的配置文件中没有要求的configkey就会返回父级的section实例,比如machine.config里的内容。
public object ResolveConfig(string configKey)
然后就是Evaluate及其后续操作了,主要就是将configkey分解成字符串数组,一层层地去找对应的xmlelement,找到后传给configkey对应的handler,如果该handler没有创建则反射创建,然后由该handler创建section的实例对象,返回给用户,该部分关键代码如下:
ConfigXmlDocument document1 = new ConfigXmlDocument();
document1.LoadSingleElement(this._filename, reader);
config = factory.Create(config, null, document1.DocumentElement);
现在我们就明白了当我们用System..Configurtion.ConfiguationSetting.GetConfig的时候发生过什么了。
在下一篇文章中,我会对系统内置的具体section的handler的实现做详细的阐述。
对于程序开发者而言,写配置文件是经常性的工作,如果你写了一个xx.config文件,如果没有详尽的注释,别人恐怕很难读懂,没有良好的配置架构,程序也失去了活力。在我看来,.net配置文件的特点在于反射定义和继承性。
我们访问配置文件时经常觉得配置文件的结构不太符合我们的需要,我们需要从里面更方便地获得自己定义的对象,而不仅仅是key和value,对于自定义配置文件的著述已有很多,在此不再描述,有兴趣的朋友可以访问http://ly4cn.cnblogs.com/archive/2005/09/06/231245.html。
自定义配置节其实还是在.net配置文件架构的应用而已,我们先来搞懂配置文件的结构,弄清楚.net配置文件的运行方式。下面是machine.config的一部分内容:
<configSections>
<section name="runtime" type="System.Configuration.IgnoreSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowLocation="false" />
<sectionGroup name="system.net">
<section name="authenticationModules" type="System.Net.Configuration.NetAuthenticationModuleHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</ sectionGroup>
</configSections>
SDK中<section>的定义为:
<section
name="section name"
type="configuration section handler class, assembly"
allowDefinition="Everywhere|MachineOnly|MachineToApplication"
allowLocation="true|false" />
<sectionGroup>的定义为:
<sectionGroup
name="section group name"/>
</sectionGroup>
我们来看看.net框架内是如何利用这种结构的。反编译System.dll找到GetConfig方法,在里面我们发现实际获取config的工作默认是由实现了IConfigurationSystem的DefaultConfiguationSystem类来实现的。
public static object GetConfig(string sectionName)
我们再来看DefaultConfigurationSystem,这个类主要包含了machine.config的名称路径的基本信息和一些uri操作,而实际的GetConfig的操作交给了ConfiguationRecord来处理,这个类没有实现任何接口,可见他和DefaultConfiguration是绑定在一起的。
1internal class DefaultConfigurationSystem : IConfigurationSystem
2
事实上所有的配置文件的分析和获取都是在ConfiguationRecord里实现的,作为配置文件分析的第一步,正如我们经常做的一样->加载一个配置文件,这个方法公开为 Load(filename)。DefaultConfiguationSystem的Init()方法中用machine.config创建了一个ConfiguationRecord对象,并将其作为父对象传递给当前程序的ConfiguationRecord对象,当然前提是当前程序有配置文件,比如myapp.config,然后再加载当前程序的配置文件,从而实现配置文件的继承。
void IConfigurationSystem.Init()
现在我们可以专注于ConfiguationRecord的具体实现了,Load方法中得到一个XmlTextWriter,并执行.ScanFactoriesRecursive和ScanSectionsRecursive方法。
reader1 = ConfigurationRecord.OpenXmlTextReader(filename);
if (reader1 != null)
ScanFactoriesRecursive方法会调用他的一个重载方法来解析<configSections>中的<sectionGroup>,<section>,<remove>,<clear>,我们写配置文件时大小写可不能写错哦,.NET没有做toslower之类的转换,直接就是 “== “。在这个方法里程序会将解析得到的sectiongroup以key=tagkey,value= ConfigurationRecord.GroupSingleton的方式存到EnsureFactories里,将section以key=tagkey,value=typestring的方式储存,值得注意的是,这里并没有创建实现IConfigurationSectionHandler的实例对象,而是将其类型名(比如:字符串”system.Configuration.NameValueSectionHandler”)作为值到EnsureFactories,等到后面GetConfig的时候再来反射创建。<remove>则存为ConfigurationRecord.RemovedFactorySingleton。<clear>就清空EnsureFactories。这里的tagkey是各级name的组合,比如”mygroup/mysection”这样以分隔符”/”组合的形式。应该客观地说这部分代码用了很多goto语句,可读性不是太好,但这样写也确实没有什么问题。
this.CheckRequiredAttribute(text3, "name", reader);
this.CheckRequiredAttribute(text4, "type", reader);
this.VerifySectionName(text3, reader);
string text5 = ConfigurationRecord.TagKey(configKey, text3);
if (this.HaveFactory(text5) != ConfigurationRecord.HaveFactoryEnum.NotFound)
this.EnsureFactories[text5] = text4;
goto Label_02B6;
ScanSectionsRecursive方法会解析配置文件里的section实例,并将其tagkey储存到Hashtable _unevaluatedSections中,表示尚未evaluated的configkey的集合,可见section实例对象的创建和handler一样,都是fetch when need。在后面的操作Getconfig中会使用他。
if (this._unevaluatedSections == null)
this._unevaluatedSections[text2] = null;
现在我们就可以看Getconfig方法是怎么来执行得到我们想要的对象的。
public object GetConfig(string configKey)
如果_result中有对应configkey的section实例,就返回,没有则需要对configkey进行ResolveConfig,将解析到的对象保存到_result中并返回给用户。在ResolveConfig方法中,可以看到如果当前的配置文件中没有要求的configkey就会返回父级的section实例,比如machine.config里的内容。
public object ResolveConfig(string configKey)
然后就是Evaluate及其后续操作了,主要就是将configkey分解成字符串数组,一层层地去找对应的xmlelement,找到后传给configkey对应的handler,如果该handler没有创建则反射创建,然后由该handler创建section的实例对象,返回给用户,该部分关键代码如下:
ConfigXmlDocument document1 = new ConfigXmlDocument();
document1.LoadSingleElement(this._filename, reader);
config = factory.Create(config, null, document1.DocumentElement);
现在我们就明白了当我们用System..Configurtion.ConfiguationSetting.GetConfig的时候发生过什么了。
在下一篇文章中,我会对系统内置的具体section的handler的实现做详细的阐述。
相关文章推荐
- .NET配置文件解析过程详解
- .NET配置文件解析过程详解(二)
- .NET配置文件解析过程详解
- .NET配置文件解析过程详解
- 【转】.NET配置文件解析过程详解(二)
- .NET配置文件解析过程详解(二)
- .NET配置文件解析过程详解
- Mybatis配置文件解析过程详解
- Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(上)
- Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(下)
- twemproxy源码解析系列三----Twemproxy配置文件解析及相关组件初始化过程
- Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(上)
- Android 从本地服务器下载文件与解析过程详解(五)
- Android init.rc文件解析过程详解(一)
- Android init.rc文件解析过程详解(二)
- [MySQL]利用apache第三方jar包,编写的的JDBCUtils工具类过程及内容详解.附带配置文件的详细注解.
- 【怎样写代码】小技巧 -- .NET配置文件详解
- keepalived配置文件解析系列之(三)配置文件解析过程
- ibatis配置文件解析过程中对DTD的加载处理
- Android中measure过程、WRAP_CONTENT详解以及 xml布局文件解析流程浅析