代码级浅析企业库对象创建
本来是没有打算写这篇分析的,但是在我分析缓存组件的时候,发现企业库的所有组件都是通过一种方式创建出来的,这就让我产生了好奇,于是决定去看看他到底是如何通过配置文件将正确的对象创建出来.
这里有四个重要的接口,一句话概括,就是配置器(IContainerConfigurator)以特定的解析方式(ITypeRegistrationsProvider)将源(IConfigurationSource)里的信息解析出来,最终发布为服务定位器(IServiceLocator).
一.服务定位器
可以从图上看到,其实所谓的企业库服务定位器,其实就是对依赖注入框架Unity的一个封装,通过GetInstance<T>方法来解析接口及其实现类的匹配.
二.配置器
配置器是一个完成类型注册的过程.从图上可以看到,Unity容器内的注册信息是通过UnityContainerConfigurator来进行注册的.进行注册的代码为:
其本质是向Unity容器进行类型注册.注册完成之后,就可以通过UnityServiceLocator向外提供服务了.
三.配置源
我们的配置信息一般写在系统的配置文件中,而IconfigurationSource接口则负责从文件中读取相关信息.从上图可以看到,真正的实现类是SystemConfigurationSource类. 在其里面有这样一句代码:
这也决定了系统只能从默认的配置文件中读取信息.
四.解析方式
可以从图中看到,所以解析,就是把配置文件分析成一个接口与实现类的映射表.TypeRegistrationProvidersConfigurationSection,TypeRegistrationProviderElementCollection与TypeRegistrationProviderElement,分别对应配置文件里的配置节,配置元素集合与配置元素,BlockSectionNames类则定义了企业库使用的配置节名称.重点看ConfigurationBasedTypeRegistrationsProviderFactory类的CreateTypeRegistrationsProviderLocators方法,代码如下:
2 if (section == null)
3 {
4 section = new TypeRegistrationProvidersConfigurationSection();
5 }
6
7 foreach (TypeRegistrationProviderElement typeRegistrationProviderElement in section.TypeRegistrationProviders)
8 {
9 if (!string.IsNullOrEmpty(typeRegistrationProviderElement.SectionName) &&
10 !string.IsNullOrEmpty(typeRegistrationProviderElement.ProviderTypeName))
11 {
12 throw new ConfigurationErrorsException(
13 string.Format("Type Registration Provider Settings '{0}' cannot declare both sectionName and providerType attributes",
14 typeRegistrationProviderElement.Name));
15 }
16 if (!string.IsNullOrEmpty(typeRegistrationProviderElement.SectionName))
17 {
18 yield return new ConfigSectionLocator(typeRegistrationProviderElement.SectionName, reconfiguringEventSource);
19 }
20 else if (!string.IsNullOrEmpty(typeRegistrationProviderElement.ProviderTypeName))
21 {
22 yield return new TypeLoadingLocator(typeRegistrationProviderElement.ProviderTypeName, reconfiguringEventSource);
23 }
24 }
第四行新建了一个TypeRegistrationProvidersConfigurationSection对象,其TypeRegistrationProviders属性在返回时,会创建库业库默认使用的节点集合,代码如下:
{
BaseAdd(new TypeRegistrationProviderElement() { Name = TypeRegistrationProvidersConfigurationSection.CachingTypeRegistrationProviderName, SectionName = BlockSectionNames.Caching });
BaseAdd(new TypeRegistrationProviderElement() { Name = TypeRegistrationProvidersConfigurationSection.CryptographyTypeRegistrationProviderName, SectionName = BlockSectionNames.Cryptography });
BaseAdd(new TypeRegistrationProviderElement() { Name = TypeRegistrationProvidersConfigurationSection.ExceptionHandlingTypeRegistrationProviderName, SectionName = BlockSectionNames.ExceptionHandling });
BaseAdd(new TypeRegistrationProviderElement() { Name = TypeRegistrationProvidersConfigurationSection.InstrumentationTypeRegistrationProviderName, SectionName = BlockSectionNames.Instrumentation });
BaseAdd(new TypeRegistrationProviderElement() { Name = TypeRegistrationProvidersConfigurationSection.LoggingTypeRegistrationProviderName, SectionName = BlockSectionNames.Logging });
BaseAdd(new TypeRegistrationProviderElement() { Name = TypeRegistrationProvidersConfigurationSection.PolicyInjectionTypeRegistrationProviderName, SectionName = BlockSectionNames.PolicyInjection });
BaseAdd(new TypeRegistrationProviderElement() { Name = TypeRegistrationProvidersConfigurationSection.SecurityTypeRegistrationProviderName, SectionName = BlockSectionNames.Security });
BaseAdd(new TypeRegistrationProviderElement() { Name = TypeRegistrationProvidersConfigurationSection.DataAccessTypeRegistrationProviderName, ProviderTypeName = BlockSectionNames.DataRegistrationProviderLocatorType });
BaseAdd(new TypeRegistrationProviderElement() { Name = TypeRegistrationProvidersConfigurationSection.ValidationTypeRegistrationProviderName, ProviderTypeName = BlockSectionNames.ValidationRegistrationProviderLocatorType });
}
下面就是读取这些节点的信息了, 代码第18行与第22行就是跟据不同的情况创建不同的定位器.
到目前为止, 只是知道了企业库会使用哪些配置节,那具体的分析工作呢,还是回到UnityContainerConfigurator类吧:
2 {
3 EnterWriteLock();
4 try
5 {
6 foreach (var registration in rootProvider.GetRegistrations(configurationSource))
7 {
8 Register(registration);
9 }
10 }
11 finally
12 {
13 ExitWriteLock();
14 }
15 }
可以看到由ITypeRegistrationsProvider接口完成.而TypeRegistrationsProvider抽象类实现了这个接口,TypeLoadingLocator与ConfigSectionLocator又继承了这个抽象类.随便看一个吧:
{
return GetRegistrationsInternal(configurationSource, (p, cs) => p.GetRegistrations(cs));
}
2 Func<ITypeRegistrationsProvider, IConfigurationSource, IEnumerable<TypeRegistration>> registrationsAccessor)
3 {
4 ITypeRegistrationsProvider provider = null;
5 ConfigurationSection section = configurationSource.GetSection(Name);
6 if (section != null)
7 {
8 provider = section as ITypeRegistrationsProvider;
9 }
10
11 if (provider != null)
12 {
13 return registrationsAccessor(provider, configurationSource);
14 }
15 return Enumerable.Empty<TypeRegistration>();
16 }
其首先从源中获取指定节点,然后把其强转成ITypeRegistrationsProvider再调用其GetRegistrations方法获取真正的配置信息.比如缓存配置,用GetSection获取后其实就是CacheManagerSettings类,其实现了ITypeRegistrationsProvider接口,调用其GetRegistrations方法解析缓存配置.
是不是感觉很乱?反正我是这么认为的.搞不懂不就是解析一个配置文件嘛,干嘛要搞这么复杂,难道不多转几个弯就不能体现企业库的NB与价值?!个人感觉最乱的就是对于ITypeRegistrationsProvider接口的使用.UnityContainerConfigurator类调用ITypeRegistrationsProvider接口,实际是调用CompositeTypeRegistrationsProviderLocator类.这个类的内部维护了一个ITypeRegistrationsProvider接口集合,其GetRegistrations方法就是遍例这个集合逐个调用各自GetRegistrations方法.实际上这个集合装着的是TypeLoadingLocator类与ConfigSectionLocator类,那么调用的就是这两个类的方法.然后这两个类又把源获取一遍,把获取的结果转成ITypeRegistrationsProvider接口,然后再调转换后的GetRegistrations方法.到了这一步,才真正把该拿的信息拿出来!有必要搞的这么复杂吗?!
参考的文章: