Castle學習筆記之Windsor(二)

最近瑣事繁多,以至於這個系列的第二篇文章都遲遲未出,-_-!!
今天特定花點時間來完成它.

我們接着上篇進行一些深入的分析.
首先是構造注入,在Windsor中,我們獲取的一個對象實例的代碼大致如下:

IWindsorContainer container = new WindsorContainer("../../test.xml");
container.AddComponent(
"test"typeof(TestObj));
TestObj obj 
= (TestObj)container["test"];

配置文件如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<components>
    
<component id="test">
      
<parameters>
        
<key>key</key>
      
</parameters>
    
</component>
  
</components>
</configuration>

具體上面的代碼是什麼意思,在上篇文章已經寫的比較清楚了,這裏就不再多說,現在我們開始分析,爲什麼只是單單的一個AddComponent就可以直接獲取對象實例了.

很自然的,我們從AddComponent函數入手分析.在Container裏只有簡單的:_kernel.AddComponent
看來這只是層很淺顯的封裝,關鍵操作始終是要在kernel裏進行.轉入DefaultKernel,因爲這裏的_kernel只是個接口,從WindsorContainer的默認構造函數:

        public WindsorContainer() : this(new DefaultKernel(), new Installer.DefaultComponentInstaller())
        
{
        }

可以很容易看出該接口的實例是個DefauleKernel對象.
進入它的AddComponent函數查看,發現只有幾行子代碼,又是封裝.在大型的框架裏,爲了保持整體結構的完整即使一個很小的功能也會有若干層嵌套,這會給設計者以後的擴展提供便利同時也會給學習者增加難度,我們就耐着性子一步步往下看吧:)

        public virtual void AddComponent(String key, Type classType)
        
{
            
if (key == nullthrow new ArgumentNullException("key");
            
if (classType == nullthrow 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.該方法如下:

                protected virtual void InitializeContributors()
        
{
            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函數,出現了我們希望看到的關鍵性代碼:

            foreach(IContributeComponentModelConstruction contributor in contributors)
            
{
                contributor.ProcessModel( kernel, model );
            }

以前已經說了,構造函數依賴關係的處理已經被加入contributors隊列,接下來,我們開始進入ConstructorDependenciesModelInspector的ProcessModel方法進行分析.

        public virtual void ProcessModel(IKernel kernel, ComponentModel model)
        
{
            
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的索引,

        public virtual object this[String key]
        
{
            
get
            
{
                
if (key == nullthrow 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()
這裏停下來了,因爲最關鍵的注入就在這裏了,我們開始仔細分析

        protected virtual object Instantiate()
        
{
            ConstructorCandidate candidate 
= SelectEligibleConstructor();
    
            Type[] signature;
            
object[] arguments = CreateConstructorArguments( candidate, out signature );
    
            
return CreateInstance(arguments, signature);
        }

首先是取出之前生成的ConstructorCandidate對象,然後進入CreateConstructorArguments函數,從下面的返回我們已經可以斷定注入就在這個函數裏.
CreateConstructorArguments

        protected virtual object[] CreateConstructorArguments( ConstructorCandidate constructor, out Type[] signature )
        
{
        
//
            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本身的優秀是不容置疑的,只不過各種框架都有其適用的範圍和方向,我們在開發中一定要根據實際情況來判斷使用何種方式.(爲了寫這篇文章,加班了半小時啊,各位看完的朋友一定多多提意見那)

發佈了2 篇原創文章 · 獲贊 0 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章