Spring ActionScript入门教程(3)-Spring和Cairngorm的结合使用[上]

在前两篇文章中RIAMeeting给大家介绍了Spring ActionScript的基本概念并完成了一个简单的例子,在这个章节,RIAMeeting将结合Cairngorm完成一个RSS阅读器的实例(Spring ActionScript还有一个针对Cairngorm的项目叫做Spring ActionScript Cairngorm,针对PureMVC的项目也在开发中),在这个过程中我们将会了解IoC和MVC框架的各自的优势以及如何将它们结合在一起来构建一个真实的项目。www.riameeting.com出品[请勿转载]

图1:Demo界面www.riameeting.com出品[请勿转载]

演示地址:www.riameeting.com出品[请勿转载]

http://www.riameeting.com/examples/SpringASCairngormDemo/SpringActionScriptCairngormDemo.html

提示:你可以在Demo上点击右键查看源码。

准备知识:

  1. Spring ActionScript简介
  2. 使用Spring ActionScript开发一个简单的实例

关于Cairngorm的简单介绍:

Cairngorm是指导Flex开发以MVC(Model-View-Control)模式进行的架构框架,这也是Adobe官方认可并推荐的一个框架。它的重点在于给你一个指导性原则,而并非具体实现,它要实现的目的就是提高项目的可维护性和灵活性,和重用(注意是功能上的重用,而非代码级的重用)。它是一个由多个设计模式组合而成的集合体。

你可以参阅这篇文章,了解关于Cairngorm的详细介绍:

http://www.riameeting.com/node/134

实现项目:RSS阅读器

接下来的内容,我们假定项目需求是实现一个RSS阅读器,这个阅读器只需实现载入并分析Feed地址,显示Feed内容即可,在这个项目中我们将综合使用Spring ActionScript和Cairngorm。

另外这个实例需要你安装Flex Builder3或者Flash Builder4,你可以在Adobe的站点下载到这些IDE。请事先了解这些IDE的基本操作知识,本文假定您已经具备了使用Flex Builder的基本技巧,在操作环节上不会做过于细致的介绍。

Part1 新建项目

首先打开Flex Builder,创建两个项目:

  1. 第一个项目,名为“SpringActionScriptCairngormDemo”,类型是Flex项目,保持默认选项,直接点击Finish完成项目创建。
  2. 第二个项目,名为“SpringActionScriptCairngormDemoLib”,注意类型是Flex库项目(Flex Library Project)。

这里介绍一下为什么要创建两个项目,第一个项目是我们要用的主项目,在这个项目中我们将使用IoC容器,使用反射的机制根据配置文件从SWF中找到所需的类并实例化,这就需要最终编译的SWF文件中应当包含所有用到的类(Flex类库和项目自己的类库),如果我们将项目自己的类库也作为SWC形式存在将非常有利于编译到主SWF,这也是第二个Lib项目存在的原因;第二个项目做为一个库项目存在,包含了这个RSS阅读器项目的主体代码和实现,我们的主要代码工作也是在这个Lib库里面,第一个项目只是完成最终的装配工作。

Part2 配置libs

同前一章类似,我们也需要将项目依赖的SWC文件放到项目的libs目录以便编译时引用,请先下载下面的压缩包:

http://www.riameeting.com/files/libs_0.rar

下载后解压,你会看到一些SWC文件。

对于项目“SpringActionScriptCairngormDemo”,直接将这些文件拷贝到这个项目的libs目录中即可。

对于项目“SpringActionScriptCairngormDemoLib”,因为这个库项目本身不包含libs目录,我们需要在项目中手工创建这样一个目录,拷贝SWC文件到这个目录中,然后对项目做一下设置,在Build Path部分加入这个包含SWC的目录:

图2:Flex Library Build Path

Part3 实现SpringActionScriptCairngormDemoLib

在第三部分我们来实现Lib项目的编码,让我们回到“SpringActionScriptCairngormDemoLib”项目。

3.1 构建MVC目录结构

这个项目是基于MVC架构的,那么我们首先完成一些目录结构的创建,在src目录下,先创建包com.riameeting,(即先创建一个目录com,然后在com里创建一个目录riameeting),在riameeting目录中创建以下的目录结构,注意中文部分是说明文字:

  1. business 这个目录用于存放服务的代理,代理将组件与服务之间进行了解耦
  2. command 这个目录用于存放命令,在MVC结构中通常命令承担主要的逻辑控制工作
  3. event 存放自定义事件
  4. factories 这个目录用于存放工厂类,工厂类将承担一部分的组装工作
  5. model 存放数据模型,通常将MVC结构中的ModelLocator放在这个目录下
  6. utils 存放自定义的一些支持项目的类
  7. view 存放视图

3.2 实现model

首先我们先设计好项目的数据模型,对这个项目来说,基本上数据就是两个:Feed地址,和Feed的XML内容,那么我们先声明一个接口,定义对这两条数据的存放。

在model下创建一个接口,名为IApplicationModel,并声明获取Feed地址和Feed内容的方法:

  1. package com.riameeting.model
  2. {
  3. import mx.collections.XMLListCollection;
  4. [Bindable]
  5. public interface IApplicationModel
  6. {
  7. //
  8. function get feedURL():String;
  9. function set feedURL(url:String):void;
  10. //
  11. function get feedList():XMLListCollection;
  12. function set feedList(dataList:XMLListCollection):void;
  13. }
  14. }

创建类ApplicationModel,实现接口IApplicationModel,并使用单例模式。

  1. package com.riameeting.model
  2. {
  3. import mx.collections.XMLListCollection;
  4. [Bindable]
  5. public class ApplicationModel implements IApplicationModel
  6. {
  7. public function ApplicationModel(enforcer:SingletonEnforcer){}
  8.  
  9. private static var me:ApplicationModel;
  10. public static function getInstance():ApplicationModel {
  11. if(me==null) {
  12. me = new ApplicationModel(new SingletonEnforcer);
  13. }
  14. return me;
  15. }
  16.  
  17. private var _feedURL:String;
  18.  
  19. public function get feedURL():String
  20. {
  21. return _feedURL;
  22. }
  23.  
  24. public function set feedURL(url:String):void
  25. {
  26. _feedURL = url;
  27. }
  28.  
  29. private var _feedList:XMLListCollection;
  30.  
  31. public function get feedList():XMLListCollection
  32. {
  33. return _feedList;
  34. }
  35.  
  36. public function set feedList(dataList:XMLListCollection):void
  37. {
  38. _feedList = dataList;
  39. }
  40.  
  41. }
  42. }
  43. class SingletonEnforcer{}

我们在创建一个接口,IApplicationModelAware,这个接口的设计含义是,如果其它的类需要在自己内部定义ApplicationModel并等待IoC来注入,就可以实现这个接口,比如我有个Command类,可以实现这个接口来在内部定义ApplicationModel,虽然实际上我也可以通过ApplicationModel.getInstance()的方法获取Model的实例,但这样会造成你的类对ApplicationModel类产生直接的依赖,这就形成了耦合,并且非常不利于单元测试,合理的做法应该是通过IoC将Model注入到你的类,而不是你的类主动去寻找Model。

IoC的理念可以总结为:不要来找我,需要的时候我会给你。

  1. package com.riameeting.model
  2. {
  3. public interface IApplicationModelAware
  4. {
  5. function get applicationModel():IApplicationModel;
  6. function set applicationModel(value:IApplicationModel):void;
  7. }
  8. }

3.3 创建自定义事件

在event目录下,我们来定义事件。如果你了解Cairngorm的一些知识,就会知道Cairngorm的基本逻辑控制都是通过事件与命令之间的映射来完成的。这里我们来定义一个事件,命名为GetFeedEvent,如果系统想获取Feed信息,只要派发这个事件即可,这个事件会被前端控制器(FrontControler)映射到一个具体的命令上。

  1. package com.riameeting.event
  2. {
  3. import com.adobe.cairngorm.control.CairngormEvent;
  4.  
  5. public class FeedEvent extends CairngormEvent
  6. {
  7. public static var GET_FEED_EVENT:String = "getFeedEvent";
  8.  
  9. public function FeedEvent(type:String)
  10. {
  11. super(type, true, true);
  12. }
  13.  
  14. }
  15. }

3.4 构建视图

在view目录下,这里我们构建两个视图,在Flex的角度考虑,其实就是构建两个MXML组件(Components),组件一是一个地址栏,允许用户输入一个Feed地址,然后按下按钮去获取Feed信息;组件二是一个列表,用于显示Feed的RSS内容,这里我们可放一个List组件,列出RSS的所有内容,并自定义项目渲染器,用标题加摘要的形式来显示RSS的一条内容。

组件一:AddressBar.mxml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="40">
  3.  
  4. <mx:Script>
  5. <![CDATA[
  6. import com.riameeting.event.FeedEvent;
  7. import com.riameeting.model.IApplicationModel;
  8. [Bindable]
  9. public var model:IApplicationModel;
  10.  
  11. private function onClick():void {
  12. model.feedURL = txt.text;
  13. var getFeedEvent:FeedEvent = new FeedEvent(FeedEvent.GET_FEED_EVENT);
  14. getFeedEvent.dispatch();
  15. }
  16. ]]>
  17. </mx:Script>
  18.  
  19. <mx:TextInput id="txt" text="{model.feedURL}" x="10" y="10" width="331"/>
  20. <mx:Button x="349" y="10" label="Go" click="onClick()"/>
  21.  
  22. </mx:Canvas>

组件二:SearchResult.mxml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="400">
  3.  
  4. <mx:Script>
  5. <![CDATA[
  6. import com.riameeting.model.IApplicationModel;
  7. [Bindable]
  8. public var model:IApplicationModel;
  9. ]]>
  10. </mx:Script>
  11.  
  12. <mx:List x="10" y="10" dataProvider="{model.feedList}" width="380" height="380">
  13. <mx:itemRenderer>
  14. <mx:Component>
  15. <mx:Box width="100%" height="100" horizontalScrollPolicy="off" verticalScrollPolicy="off">
  16. <mx:Label text="{data.title}" fontWeight="bold"/>
  17. <mx:Text htmlText="{data.description}"/>
  18. </mx:Box>
  19. </mx:Component>
  20. </mx:itemRenderer>
  21. </mx:List>
  22.  
  23. </mx:Canvas>

3.5 实现代理

在business目录下,我们来实现服务的代理。通常一个Cairngorm应用的形式是,将Command和Service隔离,Command通过调用代理,而代理调用Service来获取数据。经过考虑,我们这个代理所需要的方法就是一个获取Feed的方法,那么我们首先声明接口,如果其它的类(比如Command)要调用代理,那么只需要通过接口了解方法即可,而不需要了解具体的代理类如何实现。

创建接口IGetFeedDelegate

  1. package com.riameeting.business
  2. {
  3. import org.springextensions.actionscript.cairngorm.business.IBusinessDelegate;
  4.  
  5. public interface IGetFeedDelegate extends IBusinessDelegate
  6. {
  7. function getFeed(url:String):void;
  8. }
  9. }

然后创建代理类GetFeedDelegate来实现这个接口,并继承AbstractBusinessDelegate,这是一个抽象代理,包含了一些代理所需的基本属性。

  1. package com.riameeting.business
  2. {
  3. import mx.rpc.AsyncToken;
  4. import mx.rpc.IResponder;
  5.  
  6. import org.springextensions.actionscript.cairngorm.business.AbstractBusinessDelegate;
  7.  
  8. public class GetFeedDelegate extends AbstractBusinessDelegate implements IGetFeedDelegate
  9. {
  10. public function GetFeedDelegate(service:Object=null, responder:IResponder=null)
  11. {
  12. super(service, responder);
  13. }
  14.  
  15. public function getFeed(url:String):void {
  16. service.url = url;
  17. var token:AsyncToken = service.send();
  18. token.addResponder(this.responder);
  19. }
  20.  
  21. }
  22. }

3.6 实现Command

在command目录,我们来创建命令,在Cairngorm中,命令承担着绝大多数的代码工作,我们需要定义一个命令,来获取Feed的内容,一旦有事件触发这个命令,这个命令就执行并调取代理来获取数据。

不过首先我们需要创建一个命令的基类,命名为CommandBase,这个基类的目的是在命令中预先声明对ApplicationModel的定义,其它所有的命令都基于这个命令来扩展,则所有的命令都具备ApplicationModel属性,这就意味着所有的命令都在实例化的时候已经知道Model是什么,而不需要自己去寻找Model。

  1. package com.riameeting.command
  2. {
  3. import com.riameeting.model.IApplicationModel;
  4. import com.riameeting.model.IApplicationModelAware;
  5.  
  6. import org.springextensions.actionscript.cairngorm.commands.AbstractResponderCommand;
  7.  
  8. public class CommandBase extends AbstractResponderCommand implements IApplicationModelAware
  9. {
  10. public function CommandBase()
  11. {
  12. super();
  13. }
  14.  
  15. private var _applicatinModel:IApplicationModel;
  16. public function get applicationModel():IApplicationModel
  17. {
  18. return _applicatinModel;
  19. }
  20.  
  21. public function set applicationModel(value:IApplicationModel):void
  22. {
  23. _applicatinModel = value;
  24. }
  25.  
  26. }
  27. }

然后实现我们所需的获取Feed的命令,命名为GetFeedCommand:

  1. package com.riameeting.command
  2. {
  3. import com.adobe.cairngorm.control.CairngormEvent;
  4. import com.riameeting.business.IGetFeedDelegate;
  5.  
  6. import mx.collections.XMLListCollection;
  7. import mx.controls.Alert;
  8.  
  9. /**
  10. * 对这个Command来说,不需要自己去找Model,Delegate,IoC容器会为它注入
  11. * */
  12. public class GetFeedCommand extends CommandBase
  13. {
  14. import mx.managers.CursorManager;
  15. override public function execute(event:CairngormEvent):void {
  16. super.execute(event);
  17. IGetFeedDelegate(this.businessDelegate).getFeed(applicationModel.feedURL);
  18. CursorManager.setBusyCursor();
  19. }
  20.  
  21. override public function result(data:Object):void {
  22. CursorManager.removeBusyCursor();
  23. var x:XML = new XML(data.result.toString());
  24. applicationModel.feedList = new XMLListCollection(x.channel.item);
  25. trace(applicationModel.feedList.toXMLString());
  26. }
  27.  
  28. override public function fault(info:Object):void {
  29. Alert.show(info.toString(),"fault");
  30. }
  31.  
  32. }
  33. }

3.7 实现工厂类

这里我们首先引入一个问题,对于Command,我们希望将代理和Model都通过IoC注入给它,对于代理,我们也希望将Service注入进去,这都体现了IoC的设计原则,即这些注入工作都有IoC容器来完成,项目中的每个类只需要专注于自己功能的实现,而不需要自己去寻找外部依赖,所有的依赖都由IoC容器来负责注入。对于View我们可以声明属性并通过XML配置来注入,但对于Command这样只有在Event触发的时候才创建的实例,我们需要用特殊的机制来为它们注入依赖。这里我们就需要实现一个工厂类ApplicationModelAwareCommandFactory,这个工厂将取代Cairngorm默认的Command实例化方式,在Command实例化的时候同时注入依赖。

在factories目录中,创建类ApplicationModelAwareCommandFactory:

  1. package com.riameeting.factories
  2. {
  3. import com.adobe.cairngorm.commands.ICommand;
  4. import com.riameeting.command.CommandBase;
  5. import com.riameeting.model.IApplicationModel;
  6. import com.riameeting.model.IApplicationModelAware;
  7.  
  8. import org.as3commons.reflect.ClassUtils;
  9. import org.springextensions.actionscript.cairngorm.commands.ICommandFactory;
  10. import org.springextensions.actionscript.cairngorm.commands.ResponderCommandFactory;
  11.  
  12. public class ApplicationModelAwareCommandFactory extends ResponderCommandFactory implements ICommandFactory, IApplicationModelAware
  13. {
  14. private var _applicationModel:IApplicationModel;
  15.  
  16. public function ApplicationModelAwareCommandFactory()
  17. {
  18. super();
  19. }
  20.  
  21. override public function canCreate(clazz:Class):Boolean
  22. {
  23. if (ClassUtils.isSubclassOf(clazz, CommandBase)) {
  24. return super.canCreate(clazz);
  25. }
  26. return false;
  27. }
  28.  
  29. override public function createCommand(clazz:Class):ICommand
  30. {
  31. var result:CommandBase = super.createCommand(clazz) as CommandBase;
  32. result.applicationModel = _applicationModel;
  33. return result;
  34. }
  35.  
  36.  
  37. public function get applicationModel():IApplicationModel
  38. {
  39. return _applicationModel;
  40. }
  41. public function set applicationModel(value:IApplicationModel):void
  42. {
  43. _applicationModel = value;
  44. }
  45. }
  46. }

3.8 实现工具类ApplicationViewAssembler

因为IoC只负责实例化对象,并不负责为你加入显示列表,所以我们来实现一个工具类ApplicationViewAssembler,它的作用就是作为一个处理显示对象的容器,负责将这些显示对象添加到显示列表。将这个类保存在utils目录中。

  1. package com.riameeting.utils
  2. {
  3. import flash.display.DisplayObject;
  4.  
  5. import mx.core.Application;
  6. import mx.core.FlexSprite;
  7.  
  8. public class ApplicationViewAssembler
  9. {
  10. public function ApplicationViewAssembler(){}
  11.  
  12. public var elements:Array;
  13.  
  14. public function init():void{
  15. for (var i:int = 0 ; i < elements.length ; i++){
  16. (Application.application as FlexSprite).addChild(elements[i] as DisplayObject);
  17. }
  18. }
  19. }
  20. }

至此Lib部分的编码结束了,你可以编译一下,看bin目录下面是不是生产SWC文件了,在下半部分我们将在主体项目中引入这个Lib项目,并通过IoC容器装配出一个可用的RSS阅读器。

点击这里阅读下半部分内容

riadevID: 
您给予的分值: None 平均分: 8.2 ( 6 票)

请问下,我直接把您

请问下,我直接把您的代码放在一个项目里面,不用类库的方式,debug发现不能找到com.riameeting包里的类,需要怎么配置才能加载呢?

发表新评论

  • 网页地址和电子邮件地址将会被自动转换为链接。
  • 行和段被自动切分。
  • 您可以使用下面的标签来高亮显示您的评论内容: <code>, <blockcode>. 可以使用"[foo]".旁边显示标签样式 "<foo>" PHP代码可以用这样的区块来包含<?php ... ?> or <% ... %>

更多格式化选项信息

验证区域
系统验证:请回答下面的问题