最近瑣事繁多,以至於這個系列的第二篇文章都遲遲未出,-_-!!
今天特定花點時間來完成它.
我們接着上篇進行一些深入的分析.
首先是構造注入,在Windsor中,我們獲取的一個對象實例的代碼大致如下:
container.AddComponent("test", typeof(TestObj));
TestObj obj = (TestObj)container["test"];
配置文件如下:
<configuration>
<components>
<component id="test">
<parameters>
<key>key</key>
</parameters>
</component>
</components>
</configuration>
具體上面的代碼是什麼意思,在上篇文章已經寫的比較清楚了,這裏就不再多說,現在我們開始分析,爲什麼只是單單的一個AddComponent就可以直接獲取對象實例了.
很自然的,我們從AddComponent函數入手分析.在Container裏只有簡單的:_kernel.AddComponent
看來這只是層很淺顯的封裝,關鍵操作始終是要在kernel裏進行.轉入DefaultKernel,因爲這裏的_kernel只是個接口,從WindsorContainer的默認構造函數:
{
}
可以很容易看出該接口的實例是個DefauleKernel對象.
進入它的AddComponent函數查看,發現只有幾行子代碼,又是封裝.在大型的框架裏,爲了保持整體結構的完整即使一個很小的功能也會有若干層嵌套,這會給設計者以後的擴展提供便利同時也會給學習者增加難度,我們就耐着性子一步步往下看吧:)
{
if (key == null) throw new ArgumentNullException("key");
if (classType == null) throw new ArgumentNullException("classType");
ComponentModel model = ComponentModelBuilder.BuildModel(key, classType, classType, null);
RaiseComponentModelCreated(model);
IHandler handler = HandlerFactory.Create(model);
RegisterHandler(key, handler);
}
這裏包含了許多信息,不過我們只關注組件創建時的構造函數的參數如何注入,因此我們分析ComponentModel那句.
這裏面出現的ComponentModelBuilder派生自IComponentModelBuilder接口,所有的組件模型構造器都必須從該接口派生,其本身包含了AddContribuitor,RemoveContribuitor和BuildModel方法.這裏的ComponentModelBuilder是DefaultComponentModelBuilder的實例對象,除了上述的三個方法外,還額外的添加了InitializeContributors方法用於添加默認的Contributors.該方法如下:
{
AddContributor( new ConfigurationModelInspector() );//
AddContributor( new LifestyleModelInspector() );
AddContributor( new ConstructorDependenciesModelInspector() );
AddContributor( new PropertiesDependenciesModelInspector() );
AddContributor( new MethodMetaInspector() );
AddContributor( new LifecycleModelInspector() );
AddContributor( new ConfigurationParametersInspector() );
AddContributor( new InterceptorInspector() );
}
這裏我們只關心AddContributor( new ConstructorDependenciesModelInspector() );也就是構造函數依賴的處理.
這句把該模塊的處理加入到contributors隊列中.返回到BuildModel函數,出現了我們希望看到的關鍵性代碼:
{
contributor.ProcessModel( kernel, model );
}
以前已經說了,構造函數依賴關係的處理已經被加入contributors隊列,接下來,我們開始進入ConstructorDependenciesModelInspector的ProcessModel方法進行分析.
{
if (converter == null)
{
converter = (ITypeConverter)
kernel.GetSubSystem( SubSystemConstants.ConversionManagerKey );
}
Type targetType = model.Implementation;
ConstructorInfo[] constructors =
targetType.GetConstructors(BindingFlags.Public|BindingFlags.Instance);
foreach(ConstructorInfo constructor in constructors)
{
// We register each public constructor
// and let the ComponentFactory select an
// eligible amongst the candidates later
model.Constructors.Add( CreateConstructorCandidate(constructor) );
}
}
終於看到了熟悉的反射!函數本身結構很清晰,首先取出所有的構造函數,然後循環,同時給ConstructorCandidate對象賦值,這是個包含了構造信息和依賴模塊的對象(該對象包含了構造函數的參數信息).
到此爲止,組件的添加過程中與構造函數參數注入有關的部分就結束了,雖然有了個輪廓,但是關鍵的注入部分始終還沒出現,別急,我們接着往下看:)
我們直接返回到最前端,TestObj obj = (TestObj)container["test"];將真正的創建實例,這之前,TestObj對象一直都是處於未激活狀態.繼續trace…
容器的索引直接被指向_kernel的索引,
{
get
{
if (key == null) throw new ArgumentNullException("key");
if (!HasComponent(key))
{
throw new ComponentNotFoundException(key);
}
IHandler handler = GetHandler(key);
return ResolveComponent(handler);
}
}
激活的關鍵就在ResolveComponent函數,進入後才發現,需要轉入DefaultHandler的Resolve進行處理,當依賴條件滿足時即創建對象所依賴的對象是否都已經激活時,將進入生命週期管理器的Resolve函數進行處理.接下來的調用關係比較複雜,我簡單描述一下調用關係:
AbstractLifestyleManager.Resolve()->IComponentActivator.Create()->派生自AbstractComponentActivator的某類->InternalCreate()->Instantiate()
這裏停下來了,因爲最關鍵的注入就在這裏了,我們開始仔細分析
{
ConstructorCandidate candidate = SelectEligibleConstructor();
Type[] signature;
object[] arguments = CreateConstructorArguments( candidate, out signature );
return CreateInstance(arguments, signature);
}
首先是取出之前生成的ConstructorCandidate對象,然後進入CreateConstructorArguments函數,從下面的返回我們已經可以斷定注入就在這個函數裏.
CreateConstructorArguments
{
//
foreach(DependencyModel dependency in constructor.Dependencies)
{
object value = Kernel.Resolver.Resolve(Model, dependency);
arguments[index] = value;
signature[index++] = dependency.TargetType;
}
return arguments;
}
裏面我省略了些代碼,關鍵的部分就是這個循環,由object value = Kernel.Resolver.Resolve(Model, dependency);取出了指定的依賴參數的值,生成數組返回,最後CreateInstance…
終於看到了對象的實例化,構造函數的注入的過程已經全部清晰了!
在使用Windsor的容器註冊並消除耦合的簡單之下,卻隱藏着如此深刻的背景,Castle的結構的複雜可見一斑…
到這,本來還想繼續介紹些組件的註冊方式和參數的配置問題,不過發現本篇的篇幅已經夠長了,-_-!,那就留到下篇再寫吧:)
PS:說幾句題外話,在該系列的第一篇文章裏,有朋友問到Castle的性能問題,我想說的是,您看完這篇文章,應該心中就有數了吧?大型的框架專注的是企業級應用和快速開發,如果您的項目需要非常短的響應時間,那麼這種類型的框架往往都是不適應的,當然,Castle本身的優秀是不容置疑的,只不過各種框架都有其適用的範圍和方向,我們在開發中一定要根據實際情況來判斷使用何種方式.(爲了寫這篇文章,加班了半小時啊,各位看完的朋友一定多多提意見那)