代碼級淺析企業庫對象創建
本來是沒有打算寫這篇分析的,但是在我分析緩存組件的時候,發現企業庫的所有組件都是通過一種方式創建出來的,這就讓我產生了好奇,于是決定去看看他到底是如何通過配置文件將正確的對象創建出來.
這里有四個重要的接口,一句話概括,就是配置器(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方法.到了這一步,才真正把該拿的信息拿出來!有必要搞的這么復雜嗎?!
參考的文章:

浙公網安備 33010602011771號