SlideShare a Scribd company logo
1 of 20
Download to read offline
Flex IOC 框架概览
金庸 Ematrix   http://blog.csdn.net/ematrix001
Flex IOC 框架概览




控制反转(Inversion of Control,IOC),也称为依赖注入(Dependency Injection,DI),在过去几
年中已经成为流行的软件设计模式,从而导致许多 Flex 开发者投入到此类框架的探索,其中就包括
Spring ActionScript, Parsley, Flicc 和 Swiz。

概括地说,IOC 是一种软件设计模式,其中使用独立的对象负责为其它对象的字段填充正确的实现,
而不是这些对象自己负责。这样的好处是,你可以通过接口声明对象的字段,从而将对象及其实现进
行分离(所谓按契约设计)。另外,通过在对象中拆除创建逻辑,使得对象的目的更为明确。

IOC 容器都会提供一组类库,           帮助你以一致和声明的方式使用这个模式。                      把这种模式和接口进行结合,
可以帮助你创建可测试的和非常灵活的对象。对于 IOC 模式更深度的描述,请参见 Martin Fowler 的
文章 Inversion of Control Containers and the Dependency Injection pattern。

Java 和.Net 的 IOC 框架早已存在,最近在 Flex 社区内,有关这个领域的话题相当活跃。

在这篇文章中,我将介绍其中的一些 IOC 框架,简要概述它们是如何工作的,以及对它们进行比较。
基于比较不同框架实现的需要,我将使用 Spring ActionScript, Parsley, Flicc 和 Swiz 框架分别开发
同一个基准项目:ProfileViewer。




要求

为了完成本文的大部分内容,你需要以下软件和文件:

Flex 4 beta

      试用
      了解更多信息

Flash Builder 4 beta

      下载
      了解更多信息

范例文件:

      profileviewer_samples.zip (ZIP, 1 MB)

必备知识

   Flex 知识是需要的。




                                                                             2
Flex IOC 框架概览

IOC 概念

配置对象有两个最常见的方式:


     实例化对象(例如,myObject = new Object())

     获取对象(例如,var myObject = registry.getMyObject())


使用 IOC,你在一个独立层实例化应用程序对象,然后传递依赖到需要它们的对象。这种情况也有两
个常用的方式:


     setter 注入(例如,instance.myObject = new Object())

     构造函数注入(例如,instance = new Instance( new Object()) )


一个 IOC 框架通常包括三个主要部分:配置,工厂和注入机制。

配置

配置用来描述对象之间的相互关系。    描述配置最常用的一个方式是在一个文件中声明它。此文件有时
称为上下文(contex)文件。配置也可以使用元数据/注解或编程方式。

工厂

工厂解析配置并准备所有对象,一旦应用程序运行,这些对象就可以被检索。在传统的 Spring(最
流行的 Java IOC 框架)中,所有对象(我称它们为客户对象 )被 IOC 容器预先准备,在需要它们
的对象中使用接口声明对它们的依赖。在上下文配置文件中为声明的依赖设置使用特定的实现类。

注入机制

注入机制是一种手段,借助它将工厂创建的实例注入到应用程序或另一个实例。
在 Spring 中,web 应用程序通过 web.xml 文件完成此事。Spring 监听被加载的 web 应用程序
(webapp)  上下文。  此刻 Spring 启动类加载器,类加载器负责加载任何需要创建的对象。      工厂会(如
需要)实例化对象,并填充该对象的所有依赖,这些依赖在将实例返回给应用程序之前可能需要被实
例化(这也称为“将它们编织在一起”)。

类加载的工作方式在 Flex 中有所不同,因此,编织需要做的事情也不同。

对此,当前有两种选择:

     客户端对象可以从工厂请求一个对象(编织好了的)
     注入可以使用内置的 Flex 事件机制在初始化视图时来触发

你探索一些框架之后,对这些概念会更为清晰。

                                                                    3
Flex IOC 框架概览

ProfileViewer 介绍

我将使用 ProfileViewer 项目来比较各个框架,它是一个非常简单的应用程序,由两个界面组成:登
录面板和信息面板     (dashboard) ProfileViewer 使用简单的模型视图控制器
                        。                          (Model-View-Controller,
MVC)架构和表现层模型(Presentation Model)模式。

注意:此基准测试应用程序只是一个例子,它可能有其它的编码方式。它基于我所了解的一些流行模
式。如果你觉得我使用框架的方式并非它的原始目的,请让我知道。我很乐意根据反馈信息和改进建
议对程序作出调整。

所有范例的源代码都在 Google Code flex-ioc-examples project 上。

当你阅读文章的其余部分时,我强烈建议你下载并打开源代码。

高级架构

在开发图形用户界面应用程序时,通常会采用 MVC 模式。在此我不会讨论这个模式的细节,如果你
需要更多信息,请参阅 Model-view-controller 。

在此之上,我实现了一个服务层(见图 1)。这是程序集成的地方,在这里应用程序从后端系统获取
数据。在范例中,我首先创建应用程序的这部分功能。

最后,我使用了表现层模型模式,程序中每一个视图都对应有一个模型,模型中包含视图的状态和逻
辑。视图响应模式的变化,一般通过绑定表达式。这可以让你单元测试视图的逻辑。欲了解这一主题
的更多信息 ,请参见 Martin Fowler’s explanation of the Presentation Model 或 Paul Williams’
post 。




图 1.初始架构
                                                                               4
Flex IOC 框架概览


标识改进

更改 ProfileViewer 使用 IOC 框架之后,我将对象实例及其依赖的管理移交给 IOC 层(见图 2)。有
些 IOC 框架支持编织(wiring up)事件到动作,它使你能够建立一个控制器层。在适当的情况下,
我会利用 IOC 框架所提供的这个能力。




图 2.修改后的 IOC 架构




                                                            5
Flex IOC 框架概览


下面,我简要概述使用 IOC 框架之后应用程序可以改进的部分。

对象获取

当用户成功登录后,应用程序会获取两个对象回来(译者注:一个是 User 对象,一个是 Friends 对
象)。这两个对象的详细资料在不同的视图显示给用户。在信息面板的表现层模型 DashboardPM 的
准备过程中,我执行查找两个对象实例:

在 MainPM 中:

public function set authenticated( value : Boolean ) : void
{
     //..
     var locator : ModelLocator = ModelLocator.getInstance();
     dashboardPM = new DashboardPM( locator.user, locator.friends );
     //..
}

在这个例子中,ModelLocator 是一个单例模式,用于存储模型对象。

使用单例模式 ,我可以在应用程序的任何地方访问相同的对象实例,因为只有一个实例可以被声明
创建。在这种情况下,我可以安全访问 User 和 Friends,在其它任何地方使用的都是相同的实例。

虽然这是有益的,但使用单例也有缺点,其中之一,它会使得单元测试变得困难,因为在整个测试套
件期间,你都必须关注对象的生命周期。这是因为在测试用例中,单例使用静态引用存储,不具有被
垃圾收集的资格。

对象传递

尽量减少应用程序使用单例而导致影响的方法是层次性传递对象。

你可以在 DashboardPM 的构造函数中看到这种方式。它需要一个 User 模型和一个 Friends 模型。
表现层模型将这些实例传递给其子女,尽管它只是使用 User 对象。这是一个坏的实践做法,其中一
个对象依赖于它不直接使用的其它对象。

对于一个小的范例应用程序,这可能不会产生麻烦,但当你的应用程序规模增加时,可以想像,这种
方法可能需要做大量的工作。它还在你的类中增加了不应该存在的噪声。如果你可以只使用它需要的
项实例化对象,代码会更加清洁。

最后,初始 ProfileViewer 有一个表现层模型的层次结构,这样我可以传递对象,使用 IOC 框架之后
我将不再需要这个层次结构,我会将它删除。

配置服务层

对于范例应用程序来说,非视图层的配置有待增强。在这个例子中表现为类 LoginDelegate,其中创
建了自己的 RemoteObject 实例。

                                                                       6
Flex IOC 框架概览

Spring ActionScript

     框架: Spring ActionScript
     站点: http://www.herrodius.com/blog/
     开发者: Christophe Herreman
     版本: 0.71
     版权: Open source
     配置: XML
     Spring ActionScript (之前称为 Prana)是一个相当知名的框架,以成熟度而闻名, Christophe
                                                         由
      Herreman 开发。

核心概念

Spring ActionScript 对于使用过 Spring(.NET 版本或 Java 版本)的人来说应该很熟悉。在运行时加
载配置文件,这个文件给予工厂足够的信息,可以实例化任何应用程序请求的对象。

Spring ActionScript 基本配置

在基准项目上使用 Spring ActionScript 需要三个基本步骤:

  1. 创建一个 application-context.xml 文件
  2. 在程序中初始化工厂对象
  3. 在视图层(或其它任何地方)需要的地方,从工厂获取对象以供使用

对象工厂和对象配置

使用 Spring ActionScript,对象声明被添加在一个应用程序可以访问到的 XML 文件中(通常命名为
application-context.xml)。这个配置文件之后会被 XMLApplicationContext 类加载,该类是
ObjectFactory 的子类。

在我的实现中,我将这些初始化操作置于两个对象中:ContextLoader 和 Inject。

ContextLoader 获取应用程序上下文文件的路径。该文件在 XMLApplicationContext 中加载。在应用
程序的根部:

private function init() : void{
  ContextLoader.contextPath = "application-context.xml";
}

幕后 ContextLoader 正在加载 Spring ActionScript 上下文:

public static function set contextPath( value : String ) : void{
  _contextPath = value;
  applicationContext = new XMLApplicationContext( _contextPath );
  applicationContext.addEventListener( Event.COMPLETE,
handleLoadComplete );

}
  applicationContext.load();
                                                                       7
Flex IOC 框架概览


然后,在需要依赖的视图中,我已创建了一个注入标记(灵感来自一个同事在 Parsley 中的实现)。
使用这种方便的标记,我可以声明有哪些依赖要添加到视图。例如,在应用程序的根部,我有以下代
码:

<springActionscript:Inject
  property="pm"
  objectId="{ ContextIds.MAIN_CONTAINER_PM }"/>
<springActionscript:Inject
  property="controller"
  objectId="{ ContextIds.CONTROLLER }"/>

这将向 XMLApplicationContext 索要一个 id 为 controller 的对象,并赋值给此视图中的成员变量
controller。

这是一个很好的在视图层获取对象的方式。

注意:Christophe Herreman 已经发表了一篇博文,讲述使用元数据来完成这种类型的注入(类似
Swiz 框架),但性能会有些影响,因为要读取元数据,视图必须被序列化为 XML。

设置控制器

虽然 Spring ActionScript 计划发布 MVCS 扩展,但在这个版本中,我将使用自己的控制器,使用 Spring
ActionScript 将处理程序挂接(hook up)到事件源。

在最初的应用程序中,控制器监听显示列表中冒泡的事件。当它接收到一个事件时,它会查找所有的
处理程序,确定哪个处理程序可以处理这个事件。在此修改过的例子中,不再是监听显示列表,而是
在 application-context.xml 文件中配对(pair up)了事件处理程序和事件源。

要做到这一点,我添加了一个新类 ControllerPair,该类允许配对一个处理程序和事件源。处理程序
和事件源配对之后被传递给 SimpleController,在它的 init()函数中初始化每一个配对。

<object id="controller" class="com.adobe.login.control.SimpleController">
   <method-invocation name="init"></method-invocation>
   <property name="controllerItems">
      <array><ref>controllerItem</ref></array>
   </property>
 </object>


 <object id="controllerItem”
class="com.adobe.login.control.ControllerPair">
   <property name="dispatcher" ref="loginPM"/>
   <property name="handler" ref="handler"/>
 </object>


注意 method-invocation 标记,它允许声明在对象创建时要调用的函数。在这个例子中,我调用 init
()函数绑定(bind up)事件派发者和事件处理程序。                                            8
Flex IOC 框架概览


注入表现层模型

在 ProfileViewer 的非 IOC 版本中,表现层模型被配置为一个层次结构,以便对象的传递。我将删除
此层次结构,每个表现层模型只配置给对应的视图。

虽然这使得应用程序更加容易配置和测试,但也有一个缺点。在某些情况下,我将需要作出额外的努
力,以实现在不相关的表现层模型之间挂接交互。

Spring ActionScript 同时支持 setter 注入和构造函数注入。我更喜欢构造函数注入,因为它暴露了对
象中构造函数所需要的依赖。下面是 DashboardPM 配置:

<object id="dashboardPM"
class="com.adobe.dashboard.presentationModel.DashboardPM">
  <constructor-arg ref="user"/>
</object>

构造函数的参数在 XML 中声明,按照对象的构造函数所需要的相同顺序。ref 允许引用另一个在上下
文中声明的对象,在这个例子中,它引用声明的 User。

配置服务层

LoginHandler 包含委托对象的引用,委托对象又依赖于远程对象,远程对象将调用后台。

我使用 setter 注入配置它。域实例被传递进去,还有一个委托类和 AuthenticationClient,后者是一
个接口,可以检测用户是否已经登录。 MainPM 实现了 AuthenticationClient 接口。

在这个例子中,我首先建立委托,在上下文文件中它被配置使用一个远程对象,如下:

<object id="handler"
class="com.adobe.login.control.handler.LoginHandler">
  <property name="client" ref="mainPM"/>
  <property name="user" ref="user"/>
  <property name="friends" ref="friends"/>
  <property name="delegate" ref="loginDelegate"/>
</object>

<object id="loginDelegate" class="com.adobe.login.service.LoginDelegate">
  <property name="remoteObject" ref="remoteObject"/>
</object>

<object id="remoteObject" class="mx.rpc.remoting.RemoteObject">
  <property name="destination" value="SPRING_ACTIONSCRIPT_DESTINATION"/>
</object>




                                                                        9
Flex IOC 框架概览




Spring ActionScript 摘要

Spring ActionScript 是一个伟大的、成熟的 IOC 框架,并有活跃的发展路线图。它所使用的术语对于
使用过 Spring 的人应该非常熟悉。

使用 XML 来声明对象是有一点麻烦的,      有时你会在 XML 中声明类,     但这个类未被包含在 SWF 中(因
为在应用程序没有该类的直接引用)        ,这将导致 Flash Player 在运行时抛出异常。这个问题的解决方
案是创建 ActionScript 类,在上下文 XML 中声明依赖,并在应用程序中包含它。




                                                             10
Flex IOC 框架概览

Parsley

     框架: Parsley
     站点: http://www.spicefactory.org/
     开发者: Jens Halm
     版本: 2.0.0
     版权: Open source
     配置: XML/MXML/ActionScript
     Parsley 是另外一个成熟的 IOC 框架,最初灵感也来自 Spring。它最近经历了重大改写。新版
      本使用 Flex 内置特性,如绑定和元数据,使你配置项目可以有更大的选择。

核心概念

Parsley 的核心是配置上下文的思想,这来自 Spring,它使用依赖注入配置应用程序。

Parsley 中的配置提供多种选择,包括 XML 和 MXML。你可以使用 Flex 内置的 MXML 标记或来自
Parsley 库的自定义 MXML 标记。Parsley 注入机制使用元数据标记,类似 Swiz 框架。

Parsley 也带有消息模式。在很少干扰代码的前提下,你可以把对象配置为事件源或事件处理程序。
在这个例子中,我使用了这种能力而不是使用控制器模式。

Parsley 基本配置

Parsley 配置有三个基本步骤:

  1. 创建一个 Config.mxml 文件
  2. 在应用程序的根部初始化上下文
  3. 在你的视图中,添加 Inject 元数据标记完成依赖注入

对于这个例子来说,准备配置文件虽然还有其它选择,但我使用了一个 MXML 文件,其中使用 Flex
内置标记和 Parsley 标记来配置对象。这种做法的好处是在编译时将类包含进 SWF 文件,但也付出
了在应用程序编译之后不能够更新配置的代价。

对象工厂和对象配置

在 Config.mxml 中,你会看到所有的应用程序对象,从域模型对象到委托对象。声明它们有两种方式:

  1. 使用标准的 MXML
  2. 使用 Parsley 对象定义标记

在后面的章节中,我会描述这两种类型的更多细节。

设置控制器(和 LoginHandler)

没有使用我自己定义的控制器,我采用了 Parsley 的消息系统,它被设计为最少的影响你写的对象。
它使用元数据做到这一点。一个上下文可见的拥有元数据的对象,允许被 Parsley 用来编织事件源到


                                                         11
事件处理程序。
Flex IOC 框架概览


在范例应用程序中,LoginPM 是事件源,LoginAction(从 LoginHandler 更名得到)是事件处理程序。

这是 LoginPM 的代码摘录:

[Event( name="LOGIN", type="com.adobe.login.control.event.LoginEvent")]
[ManagedEvents("LOGIN")]
public class LoginPM extends EventDispatcher
{
  ...
  public function login() : void
  {
     var event : LoginEvent = new LoginEvent( username, password );
     dispatchEvent( event );
  }
}

使该类成为事件源的因素有三项,其中包括 Event 元数据标记,ManagedEvents 元数据标记和
EventDispatcher#dispatchEvent。在这三项中,只有 ManagedEvents 是 Parsley 特定的扩展。Event
元数据只是最佳实践,要想正常工作还需要 dispatchEvent。Parsley 使用 ManagedEvents 确定哪些
事件需要处理并委托给事件处理程序。

下面是 LoginAction 的代码摘录,它已经被配置作为一个事件处理程序:

public class LoginAction implements IResponder
{
  [MessageHandler]
  public function execute( event : LoginEvent ) : void
  {
     ...
  }
}

因为我为该函数添加了 MessageHandler 元数据,Parsley 会将此对象或函数作为任何 LoginEvent
类型事件的监听器。

为了使这些对象对 Parsley 可见,可以在配置文件中声明对象,并将配置文件传递给
FlexContextBuilder 或者在视图中使用 Configure 对象。

注入表现层模型

同其它所有的例子一样,我将表现层模型改为非层次性结构。更多解释信息,请参见 Spring
ActionScript 部分。

Parsley 同时支持 setter 注入和构造函数注入。正如我在 Spring ActionScript 的例子中所说的,我更
喜欢构造函数注入,因为它暴露了对象中构造函数所需要的依赖。下面是 DashboardPM 的配置:

<spicefactory:Object type="{ DashboardPM }"/>
                                                                       12
Flex IOC 框架概览


如果对象构造器需要参数,你可能要使用 Object 标记声明它,因为原始的 MXML 不支持带参构造器
的对象初始化。

要完成构造器,你需要在类中添加一些元数据:

[InjectConstructor]
public class DashboardPM
{
  public var user : User;

    public function DashboardPM( user : User )
    {
        this.user = user;
    }
    ...
}

元数据标记 InjectConstructor 通知 Parsley,为 DashboardPM 的构造器注入类型为 User 的已经声
明过的对象。

对于 setter 注入,你只需要在类中添加 Inject 元数据标记。例如,我在标准的 MXML 中 Config 的内
部声明 SummaryPM:

<dashboard:SummaryPM/>

然后在类文件的内部:

public class SummaryPM
{
   [Inject]
   public var friends : Friends;
   ...
}

Inject 标记表示一个类型为 Friends 的实例需要被注入到这个实例。

Parsley 摘要

受其它框架已经开创的一些灵感的启发,新版本的 Parsley 已经成为一个完整的 IOC 框架。另外,
它还支持模块化开发,支持卸载上下文。对于越来越多的 Flex 应用开始使用模块化开发,这将是一
个非常重要的特征。




                                                                  13
Flex IOC 框架概览

Flicc

       框架: Flicc
       站点: http://flicc.sourceforge.net/
       开发者: Mike Herron
       版本: 0.5
       版权: Open source
       配置: MXML

Flicc 是较鲜为人知的、最近刚刚发布的一个 IOC 框架。它采用一种略微不同的方法使用 MXML 文件
进行配置。在 MXML 中它也有自己的对象定义标记,允许你传递现有对象到工厂中,来完成这些对
象中依赖的添加(例如,传递到视图)   。

在 MXML 中声明依赖的好处是,它们在编译过程中就被添加到你的应用程序,从而在运行应用程序
时不可能导致运行时异常和依赖丢失。缺点是依赖不能改变,除非重新编译应用程序。

对象工厂和对象配置

在 Flicc 中,通过在视图中添加一个 Configure 标记来传递要配置的实例。你也可以传递现有的实例
并配置它们,就像我在 Spring ActionScript 实现中使用的 Inject 标记。

Flicc 基本配置

  1. 创建一个配置类,让它继承 MXMLObjectFactory。
  2. 初始化一个 Flicc 实例,并传递到配置文件中
  3. 在视图中需要的地方添加 Configure 标记

<FliccListener>
  <MxmlObjectFactory>
     <Config/>
  </MxmlObjectFactory>
</FliccListener>

在应用程序的根部添加主要的 MxmlObjectFactory 标记。它被一个 FliccListener 标记所包裹,它允
许 Flicc 捕获应用程序显示列表中 Configure 标记派发的事件。 Config.mxml 是我的配置文件。

设置控制器

类似 Parsley,Flicc 允许你在配置文件中编织事件到对象,尽管它不使用控制器的概念。

我使用这一点编织控制器到事件源,LoginPM 是事件源。当 LoginPM 派发事件时,事件被控制器捕
获,从而调用相应的处理程序。




                                                             14
Flex IOC 框架概览
<Object objectId="controller" clazz="{ SimpleController }">
  <handlers>
     <EventHandler eventName="{ LoginEvent.LOGIN }" handler="handleEvent">
        <source>
          <Ref to="loginPM"/>
        </source>
     </EventHandler>
  </handlers>

  <handlerArray>
     <List clazz="{ Array }">
        <Ref to="loginHandler"/>
     </List>
  </handlerArray>
</Object>

在控制器中,我查找相应类型的事件,并调用执行:

public function handleEvent( event : Event ) : void
{
  getHandlerForEventType( event.type ).execute( event );
}

注入表现层模型

在这个例子中,我也移除表现层模型间的层次结构。

Flicc 允许 setter 注入和构造函数注入。

Flicc 有两个基本的对象标记:Component 和 Object。Component 用于描述你要配置的现有对象。
Object 用于创建一个新实例。例如:

<Object objectId="dashboardPM" clazz="{ DashboardPM }">
  <constructor>
     <Ref to="user"/>
  </constructor>
</Object>

如同 Spring ActionScript 和 Parsley,你按照对象期待的顺序声明对象构造器所需要的参数。Ref 是
另一个对象的引用,该对象在上下文中声明,使用 to 属性标识。

Component 似曾相识,但你只能使用 setter 注入,因为 Component 描述的传递给 Flicc 的对象已经
存在:




                                                                       15
Flex IOC 框架概览
<Component objectId="main">
  <controller>
     <Ref to="controller"/>
  </controller>
  <pm>
     <Ref to="mainPM"/>
  </pm>
</Component>

在 Component 标记中,我填充了主应用程序的 controller 和 pm 变量。

配置服务层

LoginHandler 依赖于 LoginDelegate,LoginDelegate 又依赖于 RemoteObject。

配置如下:

<Object objectId="loginDelegate" clazz="{ LoginDelegate }">
  <remoteObject>
     <Ref to="remoteObject"/>
  </remoteObject>
</Object>

<Object objectId="remoteObject" clazz="{ RemoteObject }">
  <destination>
     FLICC_DESTINATION
  </destination>
</Object>

上面的代码声明了一个新的 LoginDelegate 实例,并在其中填充了一个 RemoteObject。

Flicc 摘要

虽然 Flicc 不同于前两个框架,因为它使用 MXML 作为声明格式,但在底层所有框架都遵循相同的基
本原则。

在 MXML 中声明配置,可以节省在开发过程中的时间,因为类会被编译进应用程序,你不必处理加
载和解析外部文件。Flicc API 不同于前两个框架,开始入门可能需要些时间。




                                                                         16
Flex IOC 框架概览

Swiz

      框架: Swiz
      站点: http://code.google.com/p/swizframework/
      开发者: Chris Scott
      版本: 0.0.5
      版权: Open source
      配置: MXML + Metadata
      Swiz 是最近刚刚发布的另一个 IOC 框架。它不仅仅是一个 IOC 框架,它旨在成为丰富互联网
       应用(Rich Internet Application,RIA)架构的完整解决方案。Swiz 使用 MXML 定义对象,
       但是有着不同的注入机制。

核心概念

Swiz 有一个配置文件,使用它加载 Swiz 实例。配置文件使用标准的 MXML 声明。这既有优点,也
有缺点。优点是,类会自动加载为类路径的一部分,可以使用数据绑定来编织对象。缺点是,使用
MXML 声明创建对象,你不能创建构造函数具有参数的对象。你当然可以在 ActionScript 块中做一些
初始化,但是这将失去使用声明标记所带来的表达质量。

对于注入定义,Swiz 从类自身读取元数据来确定哪些对象需要注入其中。这是在 Java 世界中非常流
行的 Google Guice 所使用的一种方法。

使用元数据使你的对象更有意义,你的配置文件也更加清晰。

Swiz 基本配置

  1. 为编译器添加元数据参数
  2. 创建你的 bean 文件
  3. 添加元数据到你要注入依赖的类

使用 Swiz,在应用程序的根部加载你的 bean:

private function onPreInitialize() : void
{
  Swiz.getInstance().loadBeans( [ AppBeans ] );
}

然后,你有两种选择。你可以从 Swiz 请求 Bean:

Swiz.getBean( "loginHandler" )

或者你可以使用元数据:

[Autowire(bean="dashboardPM")]
public var pm : DashboardPM


                                                                  17
Flex IOC 框架概览


有关可能性的完整列表,可以参见 Swiz 的文档。有一点要记住的是,使用元数据注入的类需要位于
显示列表中,否则注入不会发生。上面的代码片段来自 DashboardPanel。

在底层,Swiz 监听任何被添加到显示列表的对象,然后使用反射来看看对象是否包含任何注入元数
据。为读取元数据,这需要序列化对象为 XML,这可能会影响性能。

设置控制器

Swiz 有一个不错的功能,允许你指定函数来处理通过 Swiz 机制派发的事件。

在 LoginHandler 中,我已经添加了以下元数据:

[Mediate(event="LOGIN", properties="username,password")]
public function login( username : String, password : String ) : void

然后在事件源(LoginPM)中,我如下派发事件:

Swiz.dispatchEvent( new LoginEvent( username, password ) );

这是完成挂接事件源到处理程序所有要做的。

注入表现层模型

正如使用其它所有框架所做的,我以非层次性结构配置表现层模型。定义如下:

<local:MainPM id="mainPM"/>
  <login:LoginPM id="loginPM"/>
  <presentationModel:DashboardPM id="dashboardPM"/>

该声明是标准的 MXML 声明,编织工作在类中完成:

public class LoginHandler implements IResponder
{
[Autowire(bean="mainPM")]
public var client : AuthenticationClient;

[Autowire]
public var user : User;

[Autowire]
public var friends : Friends;

[Autowire(bean="loginDelegate")]
public var delegate : LoginDelegate;
…
}

                                                                       18
Flex IOC 框架概览


请注意,如果自动装配的成员和 Bean 文件中的对象有着相同名称,              那么你不需要指定 bean 的名称。
在上面的例子,user 和 friends 会自动获得名为“user”和“friends”的 bean。

配置服务层

LoginHandler 依赖于 LoginDelegate,LoginDelegate 又依赖于 RemoteObject。这些装配使用和之
前相同的元数据:

public class LoginDelegate
{
[Autowire(bean="loginHandler")]
public var responder : IResponder;

[Autowire]
public var remoteObject : RemoteObject;

//…

Swiz 摘要

Swiz 使用一种新颖的方式定义你的注入。Swiz 的配置也比其它框架简单。

Swiz 对于添加到 stage 的任何对象都要执行反射,这对于一个大型的应用程序来说是相当耗时的操
作。我不知道 Swiz 将来会不会提供选择,让你可以选择是否触发这件事情,但 Aral Balkan 和
Christophe Coenraets 已经发表了一些解决办法。




                                                                     19
Flex IOC 框架概览

后续工作

本文提供了一些知名 Flex 框架 IOC 容器的快速了解。还有一些值得注意的框架并未列入其中,如
Mate 和 SmartyPants。Mate 支持 IOC,但它提供的功能不止于此。虽然我也开始准备为这篇文章
添加一些 Mate 的范例,但我想在编写之前更多了解一些。我会在 Google code 上进一步充实 Mate
范例。SmartyPants 类似 Swiz,因为它使用元数据来描述注入,值得一试。

我希望本文的信息对你有用,首先,可以作为一篇入门,告诉你在 Flex 应用程序中如何使用 IOC;
其次,也可以作为各种 IOC 框架之间的一次比较。涉及这些框架的每个功能是不可能的,所以我建
议你查看每个框架的文档,以了解更详细的信息。




这 项 工 作 许 可 使 用 Creative Commons Attribution-Noncommercial-No Derivative Works 3.0
Unported License.




关于作者




Ed Eustace 是 EMEA 地区 Adobe 专业服务的高级技术顾问。




                                                                              20

More Related Content

What's hot

Bee Style vol043のご案内
Bee Style vol043のご案内Bee Style vol043のご案内
Bee Style vol043のご案内Tsuyoshi Horigome
 
Children’s Voices for Human Rights Newsletter – Issue no.62
Children’s Voices for Human Rights Newsletter – Issue no.62Children’s Voices for Human Rights Newsletter – Issue no.62
Children’s Voices for Human Rights Newsletter – Issue no.62hrfmedia
 
独立工作流引擎(Deep Iwf)介绍
独立工作流引擎(Deep Iwf)介绍独立工作流引擎(Deep Iwf)介绍
独立工作流引擎(Deep Iwf)介绍fredyin
 
National level funding chasing.pdf
National level funding chasing.pdfNational level funding chasing.pdf
National level funding chasing.pdfElizabeth Sines
 
20090307cakephphandson 01
20090307cakephphandson 0120090307cakephphandson 01
20090307cakephphandson 01Yusuke Ando
 
Faiz Ahmed Faiz Dast-e-saba
Faiz Ahmed Faiz Dast-e-sabaFaiz Ahmed Faiz Dast-e-saba
Faiz Ahmed Faiz Dast-e-sabaHassam Rajpoot
 
Children’s Voices for Human Rights Newsletter – Issue no. 59
Children’s Voices for Human Rights Newsletter – Issue no. 59Children’s Voices for Human Rights Newsletter – Issue no. 59
Children’s Voices for Human Rights Newsletter – Issue no. 59hrfmedia
 
Children’s Voices for Human Rights Newsletter – Issue no. 55& 56
Children’s Voices for Human Rights Newsletter – Issue no. 55& 56Children’s Voices for Human Rights Newsletter – Issue no. 55& 56
Children’s Voices for Human Rights Newsletter – Issue no. 55& 56hrfmedia
 

What's hot (20)

Installing Eclipse (in tamil)
Installing Eclipse (in tamil)Installing Eclipse (in tamil)
Installing Eclipse (in tamil)
 
Article - Sourish
Article - SourishArticle - Sourish
Article - Sourish
 
29.09.11
29.09.1129.09.11
29.09.11
 
Bee Style vol043のご案内
Bee Style vol043のご案内Bee Style vol043のご案内
Bee Style vol043のご案内
 
01.09.11
01.09.1101.09.11
01.09.11
 
niconico4j
niconico4jniconico4j
niconico4j
 
Children’s Voices for Human Rights Newsletter – Issue no.62
Children’s Voices for Human Rights Newsletter – Issue no.62Children’s Voices for Human Rights Newsletter – Issue no.62
Children’s Voices for Human Rights Newsletter – Issue no.62
 
Creating Sample Android App (in tamil)
Creating Sample Android App (in tamil)Creating Sample Android App (in tamil)
Creating Sample Android App (in tamil)
 
独立工作流引擎(Deep Iwf)介绍
独立工作流引擎(Deep Iwf)介绍独立工作流引擎(Deep Iwf)介绍
独立工作流引擎(Deep Iwf)介绍
 
Sample Add Application uisng Android (in tamil)
Sample Add Application uisng Android (in tamil)Sample Add Application uisng Android (in tamil)
Sample Add Application uisng Android (in tamil)
 
National level funding chasing.pdf
National level funding chasing.pdfNational level funding chasing.pdf
National level funding chasing.pdf
 
Create Android App using web view (in tamil)
Create Android App using web view (in tamil)Create Android App using web view (in tamil)
Create Android App using web view (in tamil)
 
35th new
35th new35th new
35th new
 
Visual basic mon
Visual basic monVisual basic mon
Visual basic mon
 
20090307cakephphandson 01
20090307cakephphandson 0120090307cakephphandson 01
20090307cakephphandson 01
 
Faiz Ahmed Faiz Dast-e-saba
Faiz Ahmed Faiz Dast-e-sabaFaiz Ahmed Faiz Dast-e-saba
Faiz Ahmed Faiz Dast-e-saba
 
Children’s Voices for Human Rights Newsletter – Issue no. 59
Children’s Voices for Human Rights Newsletter – Issue no. 59Children’s Voices for Human Rights Newsletter – Issue no. 59
Children’s Voices for Human Rights Newsletter – Issue no. 59
 
Telenor. 走出去的動力。
Telenor. 走出去的動力。Telenor. 走出去的動力。
Telenor. 走出去的動力。
 
Creating List in Android App (in tamil)
Creating List in Android App (in tamil)Creating List in Android App (in tamil)
Creating List in Android App (in tamil)
 
Children’s Voices for Human Rights Newsletter – Issue no. 55& 56
Children’s Voices for Human Rights Newsletter – Issue no. 55& 56Children’s Voices for Human Rights Newsletter – Issue no. 55& 56
Children’s Voices for Human Rights Newsletter – Issue no. 55& 56
 

Viewers also liked

En la ficha insertar
En la ficha insertarEn la ficha insertar
En la ficha insertarautoKRATZ
 
把憂鬱留在沙灘上
把憂鬱留在沙灘上把憂鬱留在沙灘上
把憂鬱留在沙灘上Amy Yeh
 
Primavera, voce soprana Mirela Zafiri
Primavera, voce soprana Mirela ZafiriPrimavera, voce soprana Mirela Zafiri
Primavera, voce soprana Mirela ZafiriLiviu Craciun
 
2009 Main Black Category Cover Sheets Web
2009 Main Black Category Cover Sheets Web2009 Main Black Category Cover Sheets Web
2009 Main Black Category Cover Sheets Websmitch66
 
Hoja de vida carlos molan
Hoja de vida   carlos molanHoja de vida   carlos molan
Hoja de vida carlos molanbajistacarlos
 
Circuito París y Países Bajos oferta estudiantes 2015
Circuito París y Países Bajos oferta estudiantes 2015Circuito París y Países Bajos oferta estudiantes 2015
Circuito París y Países Bajos oferta estudiantes 2015Veleta3000
 
Evaluacion del recurso web
Evaluacion del recurso webEvaluacion del recurso web
Evaluacion del recurso webRosalinamaria2
 
Escapismo en espiral_Presentaciones: Morelia, Apatzingán
Escapismo en espiral_Presentaciones: Morelia, ApatzingánEscapismo en espiral_Presentaciones: Morelia, Apatzingán
Escapismo en espiral_Presentaciones: Morelia, ApatzingánSilla vacía Editorial
 

Viewers also liked (20)

Tp 6 caro parma
Tp 6  caro parmaTp 6  caro parma
Tp 6 caro parma
 
En la ficha insertar
En la ficha insertarEn la ficha insertar
En la ficha insertar
 
把憂鬱留在沙灘上
把憂鬱留在沙灘上把憂鬱留在沙灘上
把憂鬱留在沙灘上
 
Temple
TempleTemple
Temple
 
Sthetiks Logos
Sthetiks LogosSthetiks Logos
Sthetiks Logos
 
Primavera, voce soprana Mirela Zafiri
Primavera, voce soprana Mirela ZafiriPrimavera, voce soprana Mirela Zafiri
Primavera, voce soprana Mirela Zafiri
 
Oreidophotoshopmt
OreidophotoshopmtOreidophotoshopmt
Oreidophotoshopmt
 
2009 Main Black Category Cover Sheets Web
2009 Main Black Category Cover Sheets Web2009 Main Black Category Cover Sheets Web
2009 Main Black Category Cover Sheets Web
 
Cronograma
CronogramaCronograma
Cronograma
 
Matt Stranghoener
Matt StranghoenerMatt Stranghoener
Matt Stranghoener
 
Hoja de vida carlos molan
Hoja de vida   carlos molanHoja de vida   carlos molan
Hoja de vida carlos molan
 
Convencion 3
Convencion 3Convencion 3
Convencion 3
 
Circuito París y Países Bajos oferta estudiantes 2015
Circuito París y Países Bajos oferta estudiantes 2015Circuito París y Países Bajos oferta estudiantes 2015
Circuito París y Países Bajos oferta estudiantes 2015
 
Evaluacion del recurso web
Evaluacion del recurso webEvaluacion del recurso web
Evaluacion del recurso web
 
Via campesina guatemala conavigua
Via campesina guatemala conaviguaVia campesina guatemala conavigua
Via campesina guatemala conavigua
 
Promesa a socios nuevos
Promesa a socios nuevosPromesa a socios nuevos
Promesa a socios nuevos
 
Escapismo en espiral_Presentaciones: Morelia, Apatzingán
Escapismo en espiral_Presentaciones: Morelia, ApatzingánEscapismo en espiral_Presentaciones: Morelia, Apatzingán
Escapismo en espiral_Presentaciones: Morelia, Apatzingán
 
COrel DRAw
COrel DRAwCOrel DRAw
COrel DRAw
 
Escuela 127 invita ruptura de formatos
Escuela 127 invita ruptura de formatosEscuela 127 invita ruptura de formatos
Escuela 127 invita ruptura de formatos
 
Quimica
QuimicaQuimica
Quimica
 

More from ematrix

Flex Ioc 框架概览
Flex Ioc 框架概览Flex Ioc 框架概览
Flex Ioc 框架概览ematrix
 
Social Game
Social GameSocial Game
Social Gameematrix
 
青岛106处老建筑钢笔画欣赏
青岛106处老建筑钢笔画欣赏青岛106处老建筑钢笔画欣赏
青岛106处老建筑钢笔画欣赏ematrix
 
事件模型探究
事件模型探究事件模型探究
事件模型探究ematrix
 
Flex体系架构剖析
Flex体系架构剖析Flex体系架构剖析
Flex体系架构剖析ematrix
 
Flex For Flash Developers Ff 2006 Final
Flex For Flash Developers Ff 2006 FinalFlex For Flash Developers Ff 2006 Final
Flex For Flash Developers Ff 2006 Finalematrix
 

More from ematrix (6)

Flex Ioc 框架概览
Flex Ioc 框架概览Flex Ioc 框架概览
Flex Ioc 框架概览
 
Social Game
Social GameSocial Game
Social Game
 
青岛106处老建筑钢笔画欣赏
青岛106处老建筑钢笔画欣赏青岛106处老建筑钢笔画欣赏
青岛106处老建筑钢笔画欣赏
 
事件模型探究
事件模型探究事件模型探究
事件模型探究
 
Flex体系架构剖析
Flex体系架构剖析Flex体系架构剖析
Flex体系架构剖析
 
Flex For Flash Developers Ff 2006 Final
Flex For Flash Developers Ff 2006 FinalFlex For Flash Developers Ff 2006 Final
Flex For Flash Developers Ff 2006 Final
 

Flex Ioc 框架概览

  • 1. Flex IOC 框架概览 金庸 Ematrix http://blog.csdn.net/ematrix001
  • 2. Flex IOC 框架概览 控制反转(Inversion of Control,IOC),也称为依赖注入(Dependency Injection,DI),在过去几 年中已经成为流行的软件设计模式,从而导致许多 Flex 开发者投入到此类框架的探索,其中就包括 Spring ActionScript, Parsley, Flicc 和 Swiz。 概括地说,IOC 是一种软件设计模式,其中使用独立的对象负责为其它对象的字段填充正确的实现, 而不是这些对象自己负责。这样的好处是,你可以通过接口声明对象的字段,从而将对象及其实现进 行分离(所谓按契约设计)。另外,通过在对象中拆除创建逻辑,使得对象的目的更为明确。 IOC 容器都会提供一组类库, 帮助你以一致和声明的方式使用这个模式。 把这种模式和接口进行结合, 可以帮助你创建可测试的和非常灵活的对象。对于 IOC 模式更深度的描述,请参见 Martin Fowler 的 文章 Inversion of Control Containers and the Dependency Injection pattern。 Java 和.Net 的 IOC 框架早已存在,最近在 Flex 社区内,有关这个领域的话题相当活跃。 在这篇文章中,我将介绍其中的一些 IOC 框架,简要概述它们是如何工作的,以及对它们进行比较。 基于比较不同框架实现的需要,我将使用 Spring ActionScript, Parsley, Flicc 和 Swiz 框架分别开发 同一个基准项目:ProfileViewer。 要求 为了完成本文的大部分内容,你需要以下软件和文件: Flex 4 beta  试用  了解更多信息 Flash Builder 4 beta  下载  了解更多信息 范例文件:  profileviewer_samples.zip (ZIP, 1 MB) 必备知识 Flex 知识是需要的。 2
  • 3. Flex IOC 框架概览 IOC 概念 配置对象有两个最常见的方式:  实例化对象(例如,myObject = new Object())  获取对象(例如,var myObject = registry.getMyObject()) 使用 IOC,你在一个独立层实例化应用程序对象,然后传递依赖到需要它们的对象。这种情况也有两 个常用的方式:  setter 注入(例如,instance.myObject = new Object())  构造函数注入(例如,instance = new Instance( new Object()) ) 一个 IOC 框架通常包括三个主要部分:配置,工厂和注入机制。 配置 配置用来描述对象之间的相互关系。 描述配置最常用的一个方式是在一个文件中声明它。此文件有时 称为上下文(contex)文件。配置也可以使用元数据/注解或编程方式。 工厂 工厂解析配置并准备所有对象,一旦应用程序运行,这些对象就可以被检索。在传统的 Spring(最 流行的 Java IOC 框架)中,所有对象(我称它们为客户对象 )被 IOC 容器预先准备,在需要它们 的对象中使用接口声明对它们的依赖。在上下文配置文件中为声明的依赖设置使用特定的实现类。 注入机制 注入机制是一种手段,借助它将工厂创建的实例注入到应用程序或另一个实例。 在 Spring 中,web 应用程序通过 web.xml 文件完成此事。Spring 监听被加载的 web 应用程序 (webapp) 上下文。 此刻 Spring 启动类加载器,类加载器负责加载任何需要创建的对象。 工厂会(如 需要)实例化对象,并填充该对象的所有依赖,这些依赖在将实例返回给应用程序之前可能需要被实 例化(这也称为“将它们编织在一起”)。 类加载的工作方式在 Flex 中有所不同,因此,编织需要做的事情也不同。 对此,当前有两种选择:  客户端对象可以从工厂请求一个对象(编织好了的)  注入可以使用内置的 Flex 事件机制在初始化视图时来触发 你探索一些框架之后,对这些概念会更为清晰。 3
  • 4. Flex IOC 框架概览 ProfileViewer 介绍 我将使用 ProfileViewer 项目来比较各个框架,它是一个非常简单的应用程序,由两个界面组成:登 录面板和信息面板 (dashboard) ProfileViewer 使用简单的模型视图控制器 。 (Model-View-Controller, MVC)架构和表现层模型(Presentation Model)模式。 注意:此基准测试应用程序只是一个例子,它可能有其它的编码方式。它基于我所了解的一些流行模 式。如果你觉得我使用框架的方式并非它的原始目的,请让我知道。我很乐意根据反馈信息和改进建 议对程序作出调整。 所有范例的源代码都在 Google Code flex-ioc-examples project 上。 当你阅读文章的其余部分时,我强烈建议你下载并打开源代码。 高级架构 在开发图形用户界面应用程序时,通常会采用 MVC 模式。在此我不会讨论这个模式的细节,如果你 需要更多信息,请参阅 Model-view-controller 。 在此之上,我实现了一个服务层(见图 1)。这是程序集成的地方,在这里应用程序从后端系统获取 数据。在范例中,我首先创建应用程序的这部分功能。 最后,我使用了表现层模型模式,程序中每一个视图都对应有一个模型,模型中包含视图的状态和逻 辑。视图响应模式的变化,一般通过绑定表达式。这可以让你单元测试视图的逻辑。欲了解这一主题 的更多信息 ,请参见 Martin Fowler’s explanation of the Presentation Model 或 Paul Williams’ post 。 图 1.初始架构 4
  • 5. Flex IOC 框架概览 标识改进 更改 ProfileViewer 使用 IOC 框架之后,我将对象实例及其依赖的管理移交给 IOC 层(见图 2)。有 些 IOC 框架支持编织(wiring up)事件到动作,它使你能够建立一个控制器层。在适当的情况下, 我会利用 IOC 框架所提供的这个能力。 图 2.修改后的 IOC 架构 5
  • 6. Flex IOC 框架概览 下面,我简要概述使用 IOC 框架之后应用程序可以改进的部分。 对象获取 当用户成功登录后,应用程序会获取两个对象回来(译者注:一个是 User 对象,一个是 Friends 对 象)。这两个对象的详细资料在不同的视图显示给用户。在信息面板的表现层模型 DashboardPM 的 准备过程中,我执行查找两个对象实例: 在 MainPM 中: public function set authenticated( value : Boolean ) : void { //.. var locator : ModelLocator = ModelLocator.getInstance(); dashboardPM = new DashboardPM( locator.user, locator.friends ); //.. } 在这个例子中,ModelLocator 是一个单例模式,用于存储模型对象。 使用单例模式 ,我可以在应用程序的任何地方访问相同的对象实例,因为只有一个实例可以被声明 创建。在这种情况下,我可以安全访问 User 和 Friends,在其它任何地方使用的都是相同的实例。 虽然这是有益的,但使用单例也有缺点,其中之一,它会使得单元测试变得困难,因为在整个测试套 件期间,你都必须关注对象的生命周期。这是因为在测试用例中,单例使用静态引用存储,不具有被 垃圾收集的资格。 对象传递 尽量减少应用程序使用单例而导致影响的方法是层次性传递对象。 你可以在 DashboardPM 的构造函数中看到这种方式。它需要一个 User 模型和一个 Friends 模型。 表现层模型将这些实例传递给其子女,尽管它只是使用 User 对象。这是一个坏的实践做法,其中一 个对象依赖于它不直接使用的其它对象。 对于一个小的范例应用程序,这可能不会产生麻烦,但当你的应用程序规模增加时,可以想像,这种 方法可能需要做大量的工作。它还在你的类中增加了不应该存在的噪声。如果你可以只使用它需要的 项实例化对象,代码会更加清洁。 最后,初始 ProfileViewer 有一个表现层模型的层次结构,这样我可以传递对象,使用 IOC 框架之后 我将不再需要这个层次结构,我会将它删除。 配置服务层 对于范例应用程序来说,非视图层的配置有待增强。在这个例子中表现为类 LoginDelegate,其中创 建了自己的 RemoteObject 实例。 6
  • 7. Flex IOC 框架概览 Spring ActionScript  框架: Spring ActionScript  站点: http://www.herrodius.com/blog/  开发者: Christophe Herreman  版本: 0.71  版权: Open source  配置: XML  Spring ActionScript (之前称为 Prana)是一个相当知名的框架,以成熟度而闻名, Christophe 由 Herreman 开发。 核心概念 Spring ActionScript 对于使用过 Spring(.NET 版本或 Java 版本)的人来说应该很熟悉。在运行时加 载配置文件,这个文件给予工厂足够的信息,可以实例化任何应用程序请求的对象。 Spring ActionScript 基本配置 在基准项目上使用 Spring ActionScript 需要三个基本步骤: 1. 创建一个 application-context.xml 文件 2. 在程序中初始化工厂对象 3. 在视图层(或其它任何地方)需要的地方,从工厂获取对象以供使用 对象工厂和对象配置 使用 Spring ActionScript,对象声明被添加在一个应用程序可以访问到的 XML 文件中(通常命名为 application-context.xml)。这个配置文件之后会被 XMLApplicationContext 类加载,该类是 ObjectFactory 的子类。 在我的实现中,我将这些初始化操作置于两个对象中:ContextLoader 和 Inject。 ContextLoader 获取应用程序上下文文件的路径。该文件在 XMLApplicationContext 中加载。在应用 程序的根部: private function init() : void{ ContextLoader.contextPath = "application-context.xml"; } 幕后 ContextLoader 正在加载 Spring ActionScript 上下文: public static function set contextPath( value : String ) : void{ _contextPath = value; applicationContext = new XMLApplicationContext( _contextPath ); applicationContext.addEventListener( Event.COMPLETE, handleLoadComplete ); } applicationContext.load(); 7
  • 8. Flex IOC 框架概览 然后,在需要依赖的视图中,我已创建了一个注入标记(灵感来自一个同事在 Parsley 中的实现)。 使用这种方便的标记,我可以声明有哪些依赖要添加到视图。例如,在应用程序的根部,我有以下代 码: <springActionscript:Inject property="pm" objectId="{ ContextIds.MAIN_CONTAINER_PM }"/> <springActionscript:Inject property="controller" objectId="{ ContextIds.CONTROLLER }"/> 这将向 XMLApplicationContext 索要一个 id 为 controller 的对象,并赋值给此视图中的成员变量 controller。 这是一个很好的在视图层获取对象的方式。 注意:Christophe Herreman 已经发表了一篇博文,讲述使用元数据来完成这种类型的注入(类似 Swiz 框架),但性能会有些影响,因为要读取元数据,视图必须被序列化为 XML。 设置控制器 虽然 Spring ActionScript 计划发布 MVCS 扩展,但在这个版本中,我将使用自己的控制器,使用 Spring ActionScript 将处理程序挂接(hook up)到事件源。 在最初的应用程序中,控制器监听显示列表中冒泡的事件。当它接收到一个事件时,它会查找所有的 处理程序,确定哪个处理程序可以处理这个事件。在此修改过的例子中,不再是监听显示列表,而是 在 application-context.xml 文件中配对(pair up)了事件处理程序和事件源。 要做到这一点,我添加了一个新类 ControllerPair,该类允许配对一个处理程序和事件源。处理程序 和事件源配对之后被传递给 SimpleController,在它的 init()函数中初始化每一个配对。 <object id="controller" class="com.adobe.login.control.SimpleController"> <method-invocation name="init"></method-invocation> <property name="controllerItems"> <array><ref>controllerItem</ref></array> </property> </object> <object id="controllerItem” class="com.adobe.login.control.ControllerPair"> <property name="dispatcher" ref="loginPM"/> <property name="handler" ref="handler"/> </object> 注意 method-invocation 标记,它允许声明在对象创建时要调用的函数。在这个例子中,我调用 init ()函数绑定(bind up)事件派发者和事件处理程序。 8
  • 9. Flex IOC 框架概览 注入表现层模型 在 ProfileViewer 的非 IOC 版本中,表现层模型被配置为一个层次结构,以便对象的传递。我将删除 此层次结构,每个表现层模型只配置给对应的视图。 虽然这使得应用程序更加容易配置和测试,但也有一个缺点。在某些情况下,我将需要作出额外的努 力,以实现在不相关的表现层模型之间挂接交互。 Spring ActionScript 同时支持 setter 注入和构造函数注入。我更喜欢构造函数注入,因为它暴露了对 象中构造函数所需要的依赖。下面是 DashboardPM 配置: <object id="dashboardPM" class="com.adobe.dashboard.presentationModel.DashboardPM"> <constructor-arg ref="user"/> </object> 构造函数的参数在 XML 中声明,按照对象的构造函数所需要的相同顺序。ref 允许引用另一个在上下 文中声明的对象,在这个例子中,它引用声明的 User。 配置服务层 LoginHandler 包含委托对象的引用,委托对象又依赖于远程对象,远程对象将调用后台。 我使用 setter 注入配置它。域实例被传递进去,还有一个委托类和 AuthenticationClient,后者是一 个接口,可以检测用户是否已经登录。 MainPM 实现了 AuthenticationClient 接口。 在这个例子中,我首先建立委托,在上下文文件中它被配置使用一个远程对象,如下: <object id="handler" class="com.adobe.login.control.handler.LoginHandler"> <property name="client" ref="mainPM"/> <property name="user" ref="user"/> <property name="friends" ref="friends"/> <property name="delegate" ref="loginDelegate"/> </object> <object id="loginDelegate" class="com.adobe.login.service.LoginDelegate"> <property name="remoteObject" ref="remoteObject"/> </object> <object id="remoteObject" class="mx.rpc.remoting.RemoteObject"> <property name="destination" value="SPRING_ACTIONSCRIPT_DESTINATION"/> </object> 9
  • 10. Flex IOC 框架概览 Spring ActionScript 摘要 Spring ActionScript 是一个伟大的、成熟的 IOC 框架,并有活跃的发展路线图。它所使用的术语对于 使用过 Spring 的人应该非常熟悉。 使用 XML 来声明对象是有一点麻烦的, 有时你会在 XML 中声明类, 但这个类未被包含在 SWF 中(因 为在应用程序没有该类的直接引用) ,这将导致 Flash Player 在运行时抛出异常。这个问题的解决方 案是创建 ActionScript 类,在上下文 XML 中声明依赖,并在应用程序中包含它。 10
  • 11. Flex IOC 框架概览 Parsley  框架: Parsley  站点: http://www.spicefactory.org/  开发者: Jens Halm  版本: 2.0.0  版权: Open source  配置: XML/MXML/ActionScript  Parsley 是另外一个成熟的 IOC 框架,最初灵感也来自 Spring。它最近经历了重大改写。新版 本使用 Flex 内置特性,如绑定和元数据,使你配置项目可以有更大的选择。 核心概念 Parsley 的核心是配置上下文的思想,这来自 Spring,它使用依赖注入配置应用程序。 Parsley 中的配置提供多种选择,包括 XML 和 MXML。你可以使用 Flex 内置的 MXML 标记或来自 Parsley 库的自定义 MXML 标记。Parsley 注入机制使用元数据标记,类似 Swiz 框架。 Parsley 也带有消息模式。在很少干扰代码的前提下,你可以把对象配置为事件源或事件处理程序。 在这个例子中,我使用了这种能力而不是使用控制器模式。 Parsley 基本配置 Parsley 配置有三个基本步骤: 1. 创建一个 Config.mxml 文件 2. 在应用程序的根部初始化上下文 3. 在你的视图中,添加 Inject 元数据标记完成依赖注入 对于这个例子来说,准备配置文件虽然还有其它选择,但我使用了一个 MXML 文件,其中使用 Flex 内置标记和 Parsley 标记来配置对象。这种做法的好处是在编译时将类包含进 SWF 文件,但也付出 了在应用程序编译之后不能够更新配置的代价。 对象工厂和对象配置 在 Config.mxml 中,你会看到所有的应用程序对象,从域模型对象到委托对象。声明它们有两种方式: 1. 使用标准的 MXML 2. 使用 Parsley 对象定义标记 在后面的章节中,我会描述这两种类型的更多细节。 设置控制器(和 LoginHandler) 没有使用我自己定义的控制器,我采用了 Parsley 的消息系统,它被设计为最少的影响你写的对象。 它使用元数据做到这一点。一个上下文可见的拥有元数据的对象,允许被 Parsley 用来编织事件源到 11 事件处理程序。
  • 12. Flex IOC 框架概览 在范例应用程序中,LoginPM 是事件源,LoginAction(从 LoginHandler 更名得到)是事件处理程序。 这是 LoginPM 的代码摘录: [Event( name="LOGIN", type="com.adobe.login.control.event.LoginEvent")] [ManagedEvents("LOGIN")] public class LoginPM extends EventDispatcher { ... public function login() : void { var event : LoginEvent = new LoginEvent( username, password ); dispatchEvent( event ); } } 使该类成为事件源的因素有三项,其中包括 Event 元数据标记,ManagedEvents 元数据标记和 EventDispatcher#dispatchEvent。在这三项中,只有 ManagedEvents 是 Parsley 特定的扩展。Event 元数据只是最佳实践,要想正常工作还需要 dispatchEvent。Parsley 使用 ManagedEvents 确定哪些 事件需要处理并委托给事件处理程序。 下面是 LoginAction 的代码摘录,它已经被配置作为一个事件处理程序: public class LoginAction implements IResponder { [MessageHandler] public function execute( event : LoginEvent ) : void { ... } } 因为我为该函数添加了 MessageHandler 元数据,Parsley 会将此对象或函数作为任何 LoginEvent 类型事件的监听器。 为了使这些对象对 Parsley 可见,可以在配置文件中声明对象,并将配置文件传递给 FlexContextBuilder 或者在视图中使用 Configure 对象。 注入表现层模型 同其它所有的例子一样,我将表现层模型改为非层次性结构。更多解释信息,请参见 Spring ActionScript 部分。 Parsley 同时支持 setter 注入和构造函数注入。正如我在 Spring ActionScript 的例子中所说的,我更 喜欢构造函数注入,因为它暴露了对象中构造函数所需要的依赖。下面是 DashboardPM 的配置: <spicefactory:Object type="{ DashboardPM }"/> 12
  • 13. Flex IOC 框架概览 如果对象构造器需要参数,你可能要使用 Object 标记声明它,因为原始的 MXML 不支持带参构造器 的对象初始化。 要完成构造器,你需要在类中添加一些元数据: [InjectConstructor] public class DashboardPM { public var user : User; public function DashboardPM( user : User ) { this.user = user; } ... } 元数据标记 InjectConstructor 通知 Parsley,为 DashboardPM 的构造器注入类型为 User 的已经声 明过的对象。 对于 setter 注入,你只需要在类中添加 Inject 元数据标记。例如,我在标准的 MXML 中 Config 的内 部声明 SummaryPM: <dashboard:SummaryPM/> 然后在类文件的内部: public class SummaryPM { [Inject] public var friends : Friends; ... } Inject 标记表示一个类型为 Friends 的实例需要被注入到这个实例。 Parsley 摘要 受其它框架已经开创的一些灵感的启发,新版本的 Parsley 已经成为一个完整的 IOC 框架。另外, 它还支持模块化开发,支持卸载上下文。对于越来越多的 Flex 应用开始使用模块化开发,这将是一 个非常重要的特征。 13
  • 14. Flex IOC 框架概览 Flicc  框架: Flicc  站点: http://flicc.sourceforge.net/  开发者: Mike Herron  版本: 0.5  版权: Open source  配置: MXML Flicc 是较鲜为人知的、最近刚刚发布的一个 IOC 框架。它采用一种略微不同的方法使用 MXML 文件 进行配置。在 MXML 中它也有自己的对象定义标记,允许你传递现有对象到工厂中,来完成这些对 象中依赖的添加(例如,传递到视图) 。 在 MXML 中声明依赖的好处是,它们在编译过程中就被添加到你的应用程序,从而在运行应用程序 时不可能导致运行时异常和依赖丢失。缺点是依赖不能改变,除非重新编译应用程序。 对象工厂和对象配置 在 Flicc 中,通过在视图中添加一个 Configure 标记来传递要配置的实例。你也可以传递现有的实例 并配置它们,就像我在 Spring ActionScript 实现中使用的 Inject 标记。 Flicc 基本配置 1. 创建一个配置类,让它继承 MXMLObjectFactory。 2. 初始化一个 Flicc 实例,并传递到配置文件中 3. 在视图中需要的地方添加 Configure 标记 <FliccListener> <MxmlObjectFactory> <Config/> </MxmlObjectFactory> </FliccListener> 在应用程序的根部添加主要的 MxmlObjectFactory 标记。它被一个 FliccListener 标记所包裹,它允 许 Flicc 捕获应用程序显示列表中 Configure 标记派发的事件。 Config.mxml 是我的配置文件。 设置控制器 类似 Parsley,Flicc 允许你在配置文件中编织事件到对象,尽管它不使用控制器的概念。 我使用这一点编织控制器到事件源,LoginPM 是事件源。当 LoginPM 派发事件时,事件被控制器捕 获,从而调用相应的处理程序。 14
  • 15. Flex IOC 框架概览 <Object objectId="controller" clazz="{ SimpleController }"> <handlers> <EventHandler eventName="{ LoginEvent.LOGIN }" handler="handleEvent"> <source> <Ref to="loginPM"/> </source> </EventHandler> </handlers> <handlerArray> <List clazz="{ Array }"> <Ref to="loginHandler"/> </List> </handlerArray> </Object> 在控制器中,我查找相应类型的事件,并调用执行: public function handleEvent( event : Event ) : void { getHandlerForEventType( event.type ).execute( event ); } 注入表现层模型 在这个例子中,我也移除表现层模型间的层次结构。 Flicc 允许 setter 注入和构造函数注入。 Flicc 有两个基本的对象标记:Component 和 Object。Component 用于描述你要配置的现有对象。 Object 用于创建一个新实例。例如: <Object objectId="dashboardPM" clazz="{ DashboardPM }"> <constructor> <Ref to="user"/> </constructor> </Object> 如同 Spring ActionScript 和 Parsley,你按照对象期待的顺序声明对象构造器所需要的参数。Ref 是 另一个对象的引用,该对象在上下文中声明,使用 to 属性标识。 Component 似曾相识,但你只能使用 setter 注入,因为 Component 描述的传递给 Flicc 的对象已经 存在: 15
  • 16. Flex IOC 框架概览 <Component objectId="main"> <controller> <Ref to="controller"/> </controller> <pm> <Ref to="mainPM"/> </pm> </Component> 在 Component 标记中,我填充了主应用程序的 controller 和 pm 变量。 配置服务层 LoginHandler 依赖于 LoginDelegate,LoginDelegate 又依赖于 RemoteObject。 配置如下: <Object objectId="loginDelegate" clazz="{ LoginDelegate }"> <remoteObject> <Ref to="remoteObject"/> </remoteObject> </Object> <Object objectId="remoteObject" clazz="{ RemoteObject }"> <destination> FLICC_DESTINATION </destination> </Object> 上面的代码声明了一个新的 LoginDelegate 实例,并在其中填充了一个 RemoteObject。 Flicc 摘要 虽然 Flicc 不同于前两个框架,因为它使用 MXML 作为声明格式,但在底层所有框架都遵循相同的基 本原则。 在 MXML 中声明配置,可以节省在开发过程中的时间,因为类会被编译进应用程序,你不必处理加 载和解析外部文件。Flicc API 不同于前两个框架,开始入门可能需要些时间。 16
  • 17. Flex IOC 框架概览 Swiz  框架: Swiz  站点: http://code.google.com/p/swizframework/  开发者: Chris Scott  版本: 0.0.5  版权: Open source  配置: MXML + Metadata  Swiz 是最近刚刚发布的另一个 IOC 框架。它不仅仅是一个 IOC 框架,它旨在成为丰富互联网 应用(Rich Internet Application,RIA)架构的完整解决方案。Swiz 使用 MXML 定义对象, 但是有着不同的注入机制。 核心概念 Swiz 有一个配置文件,使用它加载 Swiz 实例。配置文件使用标准的 MXML 声明。这既有优点,也 有缺点。优点是,类会自动加载为类路径的一部分,可以使用数据绑定来编织对象。缺点是,使用 MXML 声明创建对象,你不能创建构造函数具有参数的对象。你当然可以在 ActionScript 块中做一些 初始化,但是这将失去使用声明标记所带来的表达质量。 对于注入定义,Swiz 从类自身读取元数据来确定哪些对象需要注入其中。这是在 Java 世界中非常流 行的 Google Guice 所使用的一种方法。 使用元数据使你的对象更有意义,你的配置文件也更加清晰。 Swiz 基本配置 1. 为编译器添加元数据参数 2. 创建你的 bean 文件 3. 添加元数据到你要注入依赖的类 使用 Swiz,在应用程序的根部加载你的 bean: private function onPreInitialize() : void { Swiz.getInstance().loadBeans( [ AppBeans ] ); } 然后,你有两种选择。你可以从 Swiz 请求 Bean: Swiz.getBean( "loginHandler" ) 或者你可以使用元数据: [Autowire(bean="dashboardPM")] public var pm : DashboardPM 17
  • 18. Flex IOC 框架概览 有关可能性的完整列表,可以参见 Swiz 的文档。有一点要记住的是,使用元数据注入的类需要位于 显示列表中,否则注入不会发生。上面的代码片段来自 DashboardPanel。 在底层,Swiz 监听任何被添加到显示列表的对象,然后使用反射来看看对象是否包含任何注入元数 据。为读取元数据,这需要序列化对象为 XML,这可能会影响性能。 设置控制器 Swiz 有一个不错的功能,允许你指定函数来处理通过 Swiz 机制派发的事件。 在 LoginHandler 中,我已经添加了以下元数据: [Mediate(event="LOGIN", properties="username,password")] public function login( username : String, password : String ) : void 然后在事件源(LoginPM)中,我如下派发事件: Swiz.dispatchEvent( new LoginEvent( username, password ) ); 这是完成挂接事件源到处理程序所有要做的。 注入表现层模型 正如使用其它所有框架所做的,我以非层次性结构配置表现层模型。定义如下: <local:MainPM id="mainPM"/> <login:LoginPM id="loginPM"/> <presentationModel:DashboardPM id="dashboardPM"/> 该声明是标准的 MXML 声明,编织工作在类中完成: public class LoginHandler implements IResponder { [Autowire(bean="mainPM")] public var client : AuthenticationClient; [Autowire] public var user : User; [Autowire] public var friends : Friends; [Autowire(bean="loginDelegate")] public var delegate : LoginDelegate; … } 18
  • 19. Flex IOC 框架概览 请注意,如果自动装配的成员和 Bean 文件中的对象有着相同名称, 那么你不需要指定 bean 的名称。 在上面的例子,user 和 friends 会自动获得名为“user”和“friends”的 bean。 配置服务层 LoginHandler 依赖于 LoginDelegate,LoginDelegate 又依赖于 RemoteObject。这些装配使用和之 前相同的元数据: public class LoginDelegate { [Autowire(bean="loginHandler")] public var responder : IResponder; [Autowire] public var remoteObject : RemoteObject; //… Swiz 摘要 Swiz 使用一种新颖的方式定义你的注入。Swiz 的配置也比其它框架简单。 Swiz 对于添加到 stage 的任何对象都要执行反射,这对于一个大型的应用程序来说是相当耗时的操 作。我不知道 Swiz 将来会不会提供选择,让你可以选择是否触发这件事情,但 Aral Balkan 和 Christophe Coenraets 已经发表了一些解决办法。 19
  • 20. Flex IOC 框架概览 后续工作 本文提供了一些知名 Flex 框架 IOC 容器的快速了解。还有一些值得注意的框架并未列入其中,如 Mate 和 SmartyPants。Mate 支持 IOC,但它提供的功能不止于此。虽然我也开始准备为这篇文章 添加一些 Mate 的范例,但我想在编写之前更多了解一些。我会在 Google code 上进一步充实 Mate 范例。SmartyPants 类似 Swiz,因为它使用元数据来描述注入,值得一试。 我希望本文的信息对你有用,首先,可以作为一篇入门,告诉你在 Flex 应用程序中如何使用 IOC; 其次,也可以作为各种 IOC 框架之间的一次比较。涉及这些框架的每个功能是不可能的,所以我建 议你查看每个框架的文档,以了解更详细的信息。 这 项 工 作 许 可 使 用 Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License. 关于作者 Ed Eustace 是 EMEA 地区 Adobe 专业服务的高级技术顾问。 20