Cairngorm + BlazeDS 整合 Flex + Java 的 Demo (實例)

        今天學習Cairngorm,找了份教程,寫了個Demo,以記錄並分享學習過程。

        Cairngorm的各個部分:

1.ModelLocator:在一個地方存儲程序中所有的值對象(ValueObjects,數據)並共享變量。它與HTTP Session對象相類似,不過它存儲在Flex客戶端,而不是存儲在一箇中間層程序服務器的服務器端。

2.View(視圖):一個或多個Flex組件(Button,panel,combo Box,Tile等等)綁定到一起形成的一個特定的個體,使用ModelLocator中的數據並且針對用戶的交互動作(如點擊,鼠標滑過,拖拽等)產生自定義的CairngormEvents。

3.FrontController(前端控制器):接受CairngormEvents並且將它們映射到CairngormCommands。

4.Command(命令):處理業務邏輯,調用CairngormDelegates或其他的Commands,以及更新ModelLocator中存儲的值對象和變量。

5.Delegate(委託):由一個Command創建,它將遠程過程實例化並且將結果返回給Command。

6.Service(服務):定義鏈接到遠程數據庫的遠程過程調用(HTTP,Web Services等)。

        Cairngorm的工作流程大體是這樣:

        客戶端界面由View組成的。View使用Flex的binding(綁定)來顯示ModelLocator中包含的數據。View根據諸如鼠標點擊,按鈕按下以及拖拽之類的用戶動作產生Event。這些Event被FrontController“廣播”並“監聽”,FrontController會將Event映射到Command。Command包括業務邏輯,創建所需的Delegate,處理Delegate的響應,以及更新存儲在ModelLocator中的數據。由於View是綁定到ModelLocator中的數據上的,所以當ModelLocator中的數據改變的時候View也會自動更新。Delegate調用Service並且將結果提交給Command,這一步是可選的,但是推薦這麼做。Service調用遠程數據然後將結果提交給Delegate。

        Delegate的最簡單的形式就是一箇中間人的角色。如果一個Command需要調用webservice來獲得一些數據,它將創建一個Delegate來完成這個調用。一個Command創建一個Delegate,Delegate調用一個指定的dataService,Service返回結果給Delegate,Delegate返回結果給Command。

        Delegate並不是100%必需的,但是當涉及測試&程序環境的時候它們很有幫助。相對於在Command代碼中使用查找替換改變所有的引用來測試,將一個Delegate重映射到一個測試Service更爲簡單。

        下面說我的Demo:完成用戶登錄在後臺進行驗證合法性,並反饋給前臺。

        工具及環境介紹,詳見該文章:Flex + blazeds + J2EE 數據交互入門實例教程(圖)

        版本說明:

Flex:Flex 4;

ActionScript:ActionScript 3;

Java JDK:jdk1.6.0_20

Cairngorm,我用的是版本2.2,好像Cairngorm 3已經發布了,但是官網沒提供下載。下載cairngorm2_2_1-bin.zip,我們要用的已經打包好的資源文件Cairngorm.swc,將其拷貝到Flex工程的libs目錄下面即可。

1.後臺Java服務端項目CairngormDemo工程組成:


相應代碼:

UserVO.java

package net.dreamhui.java;

public class UserVO {
	public String userName;
	public String passWord;

	// 和ActionScript對應得構造方法
	public UserVO() {
	}

	// getters & setters
	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassWord() {
		return passWord;
	}

	public void setPassWord(String passWord) {
		this.passWord = passWord;
	}
}

LoginUser.java

package net.dreamhui.java;

public class LoginUser {
	public UserVO currentUser;
	private String uName;
	private String pWord;

	// Flex端要調用的服務
	public UserVO login(UserVO par_user) {
		// UserVO par_user2 = UserVO(par_user);
		uName = par_user.userName;
		pWord = par_user.passWord;
		if (uName.equalsIgnoreCase("wwh") && pWord.equalsIgnoreCase("wwh")) {
			return par_user;
			// return "歡迎用戶:"+uName;
		} else {
			return null;
			// return "用戶名或密碼錯誤,請重新輸入";
		}
	}
}

2.前臺Flex工程CairngormDemo工程組成:


        按照 ValueObjects模型(UserVO.as)——>模型定位(UserModelLocator.as)——>視圖(loginView.mxml及主應用視圖CairngormDemo.mxml)——>事件(LoginEvent.as)——>前端控制器(LoginController.as)——>Command(LoginCommand.as)——>委派代理(LoginDelegate.as)——>服務定位(LoginServiceLocator.mxml)依次的相應代碼如下(因爲文件中註釋較多,不多寫註解):

UserVO.as

package net.dreamhui.vo
{
	/**
	 * 我看的教程裏面講綁定ValueObject到遠程Java類的方法是這樣的,
	 * public static var registered:Boolean = this.registerClass();
	 * 可能又是版本問題,在我這兒是通不過的,我到網上找到了如下的解決方法:
	 * [RemoteClass(alias="net.dreamhui.java.UserVO")],
	 * 估計是我看的教程版本太低了,呵呵
	 * */
	import com.adobe.cairngorm.vo.ValueObject;
	
	[Bindable]
	[RemoteClass(alias="net.dreamhui.java.UserVO")] 
	public class UserVO implements ValueObject
	{
		private var _userName:String;
		private var _passWord:String;
		/**
		 * 爲了實現遠程Class之間的綁定,構造方法必須形式上完全一致,
		 * 包括參數的個數和類型,否則會出現異常。
		 * */
		public function UserVO()
		{
		}
		//getters & setters
		public function get userName():String
		{
			return _userName;
		}
		public function set userName(value:String):void
		{
			_userName = value;
		}
		public function get passWord():String
		{
			return _passWord;
		}
		public function set passWord(value:String):void
		{
			_passWord = value;
		}
	}
}

UserModelLocator.as

package net.dreamhui.model
{
	import com.adobe.cairngorm.CairngormError;
	import com.adobe.cairngorm.CairngormMessageCodes;
	import com.adobe.cairngorm.model.ModelLocator;
	
	import flash.events.Event;
	
	import net.dreamhui.vo.UserVO;
	
	//綁定全局View數據
	[Bindable]
	public class UserModelLocator implements ModelLocator
	{
		private var _currentUser:UserVO;
		public static const LOGIN_YES:String = "loginYes";
		private static var instance:UserModelLocator;
		//單例模式
		
		public function UserModelLocator()
		{
			if(instance == !null)
			{
				throw new CairngormError(CairngormMessageCodes.SINGLETON_EXCEPTION,"UserModelLocator");
			}
			instance = this;
		}
		
		public static function getInstance():UserModelLocator
		{
			if(instance == null)
			{
				instance = new UserModelLocator();
			}
			return instance;
		}
		
		public function get currentUser():UserVO
		{
			return _currentUser;
		}
		/**
		 * 此賦值操作綁定loginYes事件,當輸入數據合法時候,將改變當前的登錄狀態
		 * **/
		[Bindable("loginYes")]
		public function set currentUser(value:UserVO):void
		{
			_currentUser = value;
			dispatchEvent(new Event(UserModelLocator.LOGIN_YES));
		}
	}
}
loginView.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009" 
		 xmlns:s="library://ns.adobe.com/flex/spark" 
		 xmlns:mx="library://ns.adobe.com/flex/mx"
		 title="請登陸" currentState="initState"
		 creationComplete="lvcreationComplete(event)">
	
	<!--~~~~~~~~~~~~~~~~~~~~~~Script~~~~~~~~~~~~~~~~~~~~~~-->
	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			import mx.rpc.events.FaultEvent;
			import mx.rpc.events.ResultEvent;
			import mx.validators.Validator;
			import mx.events.ValidationResultEvent;
			import mx.core.UIComponent;
			
			import net.dreamhui.control.LoginEvent;
			import net.dreamhui.model.UserModelLocator;
			import net.dreamhui.vo.UserVO;
			
			/***************************************************/
			private var validObjs:Array;
			
			/***************************************************/
			protected function lvcreationComplete(event:FlexEvent):void
			{
				validObjs = [unSV,pwSV];
			}
			protected function submit(event:MouseEvent):void
			{
				var validatorResults:Array;
				validatorResults = Validator.validateAll(validObjs);
				if(validatorResults.length == 0)
				{
					var user:UserVO = new UserVO();
					user.userName = uName.text;
					user.passWord = pWord.text;
					
					var lgEvent:LoginEvent = new LoginEvent(LoginEvent.LOGIN_USER);
					lgEvent.data = user;
					lgEvent.dispatch();
					//派發事件
					/***LoginEvent繼承自com.adobe.cairngorm.control.CairngormEvent,
					 * 用父類的方法dispatch派發事件;
					 * 其實CairngormEvent繼承自flash.events.Event
					 * dispatch是CairngormEventDispatcher類封裝IEventDispatcher
					 * 的dispatchEvent()方法
					 * **/
				}
				else
				{
					//定義校驗出錯事件
					var vEvent:ValidationResultEvent;
					//取出第一個出錯事件
					vEvent = validatorResults[0] as ValidationResultEvent;
					//將光標定位到第一個出錯的組件上
					(vEvent.target.source as UIComponent).setFocus();
				} 
			}
		]]>
	</fx:Script>
	
	<!--~~~~~~~~~~~~~~~~~~~~~~states~~~~~~~~~~~~~~~~~~~~~~-->
	<s:states>
		<s:State name="initState"/>
		<s:State name="loginState"/>
	</s:states>
	
	<!--~~~~~~~~~~~~~~~~~~~~~~Declarations~~~~~~~~~~~~~~~~~~~~~~-->
	<fx:Declarations>
		<!--定義用戶名和密碼的輸入校驗類-->
		<mx:StringValidator id="unSV"
							source="{uName}"
							property="text"
							required="true"
							maxLength="10"
							tooLongError="用戶名最長爲10位"
							requiredFieldError="請填寫用戶名" />
		<mx:StringValidator id="pwSV"
							source="{pWord}"
							property="text"
							required="true"
							maxLength="10"
							tooLongError="密碼最長爲10位"
							requiredFieldError="請填寫密碼" />
	</fx:Declarations>
	
	<!--~~~~~~~~~~~~~~~~~~~~~~UI Components~~~~~~~~~~~~~~~~~~~~~~-->
	<mx:Form includeIn="initState">
		<mx:FormItem label="用戶名" >
			<s:TextInput id="uName" />
		</mx:FormItem>
		<mx:FormItem label="密 碼" >
			<s:TextInput id="pWord" displayAsPassword="true" />
		</mx:FormItem>
		<mx:FormItem>
			<s:Button id="submitBtn" click="submit(event)"  label="登陸" right="0" />
		</mx:FormItem>
	</mx:Form>
	<s:HGroup includeIn="loginState" top="20" left="10" >
		<s:Label  text="歡迎尊貴的用戶:"/>
		<s:Label  id="cuName" text="{UserModelLocator.getInstance().currentUser.userName}" />
	</s:HGroup>
	
</s:Panel>
CairngormDemo.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" 
			   xmlns:business="net.dreamhui.business.*" 
			   xmlns:control="net.dreamhui.control.*" 
			   xmlns:view="net.dreamhui.view.*" 
			   creationComplete="creationComplete()">
	
	<!--~~~~~~~~~~~~~~~~~~~~~~Script~~~~~~~~~~~~~~~~~~~~~~-->
	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.FlexEvent;
			
			import net.dreamhui.model.UserModelLocator;
			
			/***************************************************/
			protected function creationComplete():void
			{
				UserModelLocator.getInstance().addEventListener(UserModelLocator.LOGIN_YES,switchState);
			}
			protected function switchState(event:Event):void
			{
				lgView.currentState = "loginState";
			}
			
		]]>
	</fx:Script>
	
	<!--~~~~~~~~~~~~~~~~~~~~~~Declarations~~~~~~~~~~~~~~~~~~~~~~-->
	<fx:Declarations>
		<!--初始化服務,裏面含有Command註冊 和 遠程過程調用的信息-->
		<business:LoginServiceLocator id="lgService" />
		<control:LoginController id="loginContr" />
		<!--<commands:LoginCommand id="lCommand" />-->
		<!--<model:UserModelLocator id="uLocator" />-->
	</fx:Declarations>
	
	<!--~~~~~~~~~~~~~~~~~~~~~~UI Components~~~~~~~~~~~~~~~~~~~~~~-->
	<view:loginView id="lgView" top="10" horizontalCenter="0" width="30%" 
					height="30%" fontSize="20"/>
</s:Application>
LoginEvent.as
package net.dreamhui.control
{
	import com.adobe.cairngorm.control.CairngormEvent;
	import net.dreamhui.vo.UserVO;
	
	public class LoginEvent extends CairngormEvent
	{
		//定義事件類型常量
		public static const LOGIN_USER:String = "loginUser";
		public function LoginEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}
	}
}
LoginController.as
package net.dreamhui.control
{
	import com.adobe.cairngorm.control.FrontController;
	import net.dreamhui.commands.LoginCommand;
	
	public class LoginController extends FrontController
	{
		public function LoginController()
		{
			initCommands();
		}
		/**註冊Command*/
		private function initCommands():void
		{
			addCommand(LoginEvent.LOGIN_USER,LoginCommand);
		}
	}
}
LoginCommand.as
package net.dreamhui.commands
{
	import com.adobe.cairngorm.commands.ICommand;
	import com.adobe.cairngorm.control.CairngormEvent;
	
	import mx.controls.Alert;
	import mx.rpc.IResponder;
	
	import net.dreamhui.business.LoginDelegate;
	import net.dreamhui.control.LoginEvent;
	import net.dreamhui.model.UserModelLocator;
	import net.dreamhui.vo.UserVO;
	
	public class LoginCommand implements ICommand,IResponder
	{
		public function LoginCommand()
		{
		}
		public function execute(event:CairngormEvent):void
		{
			//Alert.show("execute");
			var lgEvent:LoginEvent = LoginEvent(event);
			var user:UserVO = lgEvent.data;
			var delegate:LoginDelegate = new LoginDelegate(this);
			delegate.login(user);
		}
		/**
		 * Command實現IResponder接口的兩個方法result和fault;
		 * 我看了兩份教程,前者是這樣的:
		 * 1、Command實現com.adobe.cairngorm.business.Responder接口的onResult和onFault兩個方法;
		 * 2、Command實現mx.rpc.IResponder接口的兩個方法result和fault;
		 * 因爲我在LoginDelegate沒能實現將內部變量設置爲com.adobe.cairngorm.business.Responder,
		 * 所以,我採用了第二種方案,通過。
		 * */
		public function result(event:Object):void
		{
			var cuUser:UserVO = event.result as UserVO;
			if(cuUser)
			{
				UserModelLocator.getInstance().currentUser = cuUser;
				//此賦值操作綁定loginYes事件,當輸入數據合法時候,將改變當前的登錄狀態
				//Alert.show(cuUser.userName);
			}
			else
			{
				Alert.show("用戶名或密碼錯誤,請重新填寫");
			}
		}
		public function fault(event:Object):void
		{
			trace("服務調用錯誤"+event.toString());
		}
	}
}
LoginDelegate.as
package net.dreamhui.business
{
	import com.adobe.cairngorm.business.ServiceLocator;
	import mx.rpc.IResponder;
	import net.dreamhui.vo.UserVO;
	
	public class LoginDelegate
	{
		public var responder:IResponder;
		public var service:Object;
		
		public function LoginDelegate(responder:IResponder)
		{
			this.service = ServiceLocator.getInstance().getRemoteObject("loginService");
			//this.service = ServiceLocator.getInstance().getService("loginService");
			//我看的教程裏採用下面的做法,程序運行沒有問題,只是會有warning
			this.responder = responder;
		}
		public function login(user:UserVO):void
		{
			var call:Object = service.login(user);
			//call.resultHandler = Delegate.create(responder, responder.onResult);
			//call.faultHandler = Delegate.create(responder, responder.onFault);
			//註釋掉的部分是教程裏的寫法,可能是版本的問題,在我這兒是不對的
			call.addResponder(responder);
		}
	}
}
LoginServiceLocator.mxml

<?xml version="1.0" encoding="utf-8"?>
<rds:ServiceLocator xmlns:rds="com.adobe.cairngorm.business.*"
					xmlns:mx="http://www.adobe.com/2006/mxml">	
	<!--
<cairngorm:ServiceLocator
	xmlns:fx="http://ns.adobe.com/mxml/2009"
	xmlns:s="library://ns.adobe.com/flex/spark"
	xmlns:mx="library://ns.adobe.com/flex/mx"
	xmlns:cairngorm="http://www.adobe.com/2006/cairngorm">
	~~~~~~~~~~~~~~~~~~~~~~Declarations~~~~~~~~~~~~~~~~~~~~~~
	<fx:Declarations>
		<s:RemoteObject id="loginService"
						destination="loginUser"
						showBusyCursor="true"
						/>
	</fx:Declarations>
</cairngorm:ServiceLocator>
	-->
		<mx:RemoteObject id="loginService" destination="loginUser" showBusyCursor="true"
						 endpoint="/FlexWeb/messagebroker/amf"/>
</rds:ServiceLocator>

最後工程運行結果:


注:

1)在原文基礎上略有修改,原文地址:http://blog.dreamhui.net/archives/64

2)Q:LoginServiceLocator.mxml報錯。 xmlns:cairngorm="http://www.adobe.com/2006/cairngorm"> 這一行 Could not resolve <cairngorm:ServiceLocator> to a component implementation.

A:這個問題確定已將libs中的Cairngorm.swc添加到了構建路徑庫中後,LoginServiceLocator.mxml就用上文中的即可;反之,原代碼是註釋中的內容。

3)上文中圖片是本人自行添上的,原文中的圖顯示不了,應該與原文有所不同,還請見諒……

4)Cairngorm相關:

基於 Cairngorm MVC 框架的 Flex 程序設計與開發:http://www.ibm.com/developerworks/cn/web/1008_zhaifeng_cairngorm/

Flex Cairngorm詳解:http://www.cnblogs.com/zhainanJohnny/archive/2010/11/28/1890301.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章