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本身的优秀是不容置疑的,只不过各种框架都有其适用的范围和方向,我们在开发中一定要根据实际情况来判断使用何种方式.(为了写这篇文章,加班了半小时啊,各位看完的朋友一定多多提意见那)

posted on 2006-04-28 18:06  wiseman  阅读(4441)  评论(5编辑  收藏  举报

导航