在我之前的兩篇文章Flex3 性能提示 、Ajax輪詢以及Comet模式—寫在Servlet 3.0發布之前中。我反復提到Adobe BlazeDS這個開源產品,假設某天,你在看了Flex3 性能提示之后放棄了Flex+Servlet+XML方案,那么如何進行下一步?本文試圖用簡單的表述來實踐一回Java到Flex之間的對象透明傳遞,所謂透明本人理解為不需要人工的(反)序列化。在閱讀本文之前,你需要有一定的Spring、BlazeDS、Flex使用經驗。
我使用的是Eclipse 3.4 Java EE平臺和Tomcat6.x以及SQL Server 2005,為了不脫俗,選用了Spring(傳說中的春天框架)外加最近才GA的Spring BlazeDS Integration。PS:其實不用春天框架也可以,只是覺得他的JdbcDaoSupport很不錯,可以自己偷懶了,而且連接池和JDBC驅動這些基礎設施也all-in-one了。另外,要使用Spring BlazeDS Integration需滿足它3個愿望:Java 5 or higher、Spring 2.5.6 or higher、Adobe BlazeDS 3.2 or higher(推薦下載blazeds-turnkey),我想這三個愿望在google上是很好實現的。成品的項目結構是這樣的,對于如此簡單的實踐,無需用到Service層:
有了上面的目標,接下來就很好辦了。首先創建名為“JavaFlexMapping”的動態Web項目,接著導入“blazeds-turnkey-3.2.x.xxxx\resources\lib”下的所有jar;至于Spring相關的jar,你可以根據自己的經驗導入,也可以先導入spring.jar然后導入spring-webmvc.jar(Spring BlazeDS Integration需要),最后再把“spring-framework-2.5.6\lib”下的所有jar都導入進來,這樣干犧牲了磁盤空間,但換來了更少的煩惱;Spring BlazeDS Integration的org.springframework.flex-1.0.0.RELEASE.jar也導入進來吧。這就算完成了部分包導入工作,不過在實際使用中,還會需要其他jar:xalan.jar、backport-util-concurrent.jar、jtds-1.2.2.jar(SQL Server JDBC驅動),如果在之后的實際操作中出現缺包情況,Spring會在控制臺說明的。最后把blazeds-turnkey-3.2.x.xxxx 下的“\tomcat\webapps\blazeds\WEB-INF\flex”文件全部拷貝到WEB-INF下的flex目錄中。
Java程序的運行環境都配置好了,看看表結構是怎樣的:
這是一個簡單的表,反映了組和五個組成員的信息。
所有必要的資源準備完畢,可以編碼了,先看VO類“FlexGroup.java”的內容:
/**
* Flex Group Object
* @author rosen jiang
* @since 2009/06/21
*
*/
public class FlexGroup {
private String groupName;
private String member1;
private String member2;
private String member3;
private String member4;
private String member5;
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public String getMember1() {
return member1;
}
public void setMember1(String member1) {
this.member1 = member1;
}
public String getMember2() {
return member2;
}
public void setMember2(String member2) {
this.member2 = member2;
}
public String getMember3() {
return member3;
}
public void setMember3(String member3) {
this.member3 = member3;
}
public String getMember4() {
return member4;
}
public void setMember4(String member4) {
this.member4 = member4;
}
public String getMember5() {
return member5;
}
public void setMember5(String member5) {
this.member5 = member5;
}
}
沒啥好說的,就是一個表結構的對象化表述,不過有兩個問題。
1、既然牽涉到跨語言的對象傳輸為什么沒實現序列化接口?
為了讓自己和大家了解,昨天我專門請教了據說是Sun公司還是IBM公司的名叫toad的達人,解惑曰(未找到確鑿證據,僅僅是技術層面的推測):不實現序列化接口是因為BlazeDS在進行序列化的時候并未使用Java自己的機制。如果各位有更好的解釋,尤其是看了BlazeDS源碼的同學請過來share。
2、BlazeDS官方文檔所要求成員變量必須為public(參看http://livedocs.adobe.com/blazeds/1/blazeds_devguide/serialize_data_3.html,這些白紙黑字寫著:”Private properties, constants, static properties, and read-only properties, and so on, are not serialized. “)?
的確,聲明為private的成員變量也不會出問題,這個問題我想是因為官方文檔滯后的原因,另外就算是私有成員明顯可以通過反射獲取其值。
接下來是接口“ListDAO.java”,注意,按照規范,暴露給Flex調用的不能包含以下保留的方法名:
disconnect()
getOperation()
hasOwnProperty()
initialized()
isPrototypeOf()
logout()
propertyIsEnumerable()
setCredentials()
setPropertyIsEnumerable()
setRemoteCredentials()
toString()
valueOf()
如果你不小心用到了,可以參考BlazeDS官方文檔“RemoteObject component”章節,看如何解決。getData()方法是供Flex端調用的,傳入參數是Flex構造的FlexGroup對象實例:
import java.util.List;
/**
* DAO Interface
* @author rosen jiang
* @since 2009/06/21
*
*/
public interface ListDAO {
/**
*


*/
public List<FlexGroup> getData(FlexGroup fg);
}
然后是實現類“ListDAOImpl.java”,該類利用了JdbcDaoSupport的模板方法來處理JDBC操作:
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
/**
* DAO Impl
* @author rosen jiang
* @since 2009/06/21
*
*/
public class ListDAOImpl extends JdbcDaoSupport implements ListDAO {
/**
*


*/
public List<FlexGroup> getData(FlexGroup fg){
//test properties
System.out.println(fg.getGroupName());
System.out.println(fg.getMember1());
System.out.println(fg.getMember2());
System.out.println(fg.getMember3());
System.out.println(fg.getMember4());
System.out.println(fg.getMember5());
//Jdbc Template
JdbcTemplate jt = this.getJdbcTemplate();
List<FlexGroup> list = jt.query("select * from flex_group",
new RowMapper() {
public FlexGroup mapRow(ResultSet rs, int rowNum) throws SQLException {
FlexGroup fg = new FlexGroup();
fg.setGroupName(rs.getString("group_name"));
fg.setMember1(rs.getString("member1"));
fg.setMember2(rs.getString("member2"));
fg.setMember3(rs.getString("member3"));
fg.setMember4(rs.getString("member4"));
fg.setMember5(rs.getString("member5"));
return fg;
}
});
return list;
}
}
開始寫“web.xml”文件,注意,由于使用了Spring BlazeDS Integration,所以內容和單純使用BlazeDS不一樣,以往通過messagebroker獲取的請求,現在交由Spring處理了:
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all /messagbroker requests to the DispatcherServlet for handling -->
<servlet-mapping>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>
</web-app>
最后是“applicationContext.xml”:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:flex="http://www.springframework.org/schema/flex"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/flex
http://www.springframework.org/schema/flex/spring-flex-1.0.xsd">
<!-- Dada Source -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver"/>
<property name="url" value="jdbc:jtds:sqlserver://localhost:1433/cfd"/>
<property name="username" value="sa"/>
<property name="password" value="123456"/>
</bean>
<!-- Message Broker -->
<flex:message-broker services-config-path="/WEB-INF/flex/services-config.xml"/>
<!-- List Data Bean -->
<bean id="listDAO" class="org.rosenjiang.flex.ListDAOImpl">
<property name="dataSource">
<ref bean="dataSource"></ref>
</property>
<flex:remoting-destination/>
</bean>
</beans>
這里引入了flex命名空間,拜Spring BlazeDS Integration所賜,僅僅需要一個“<flex:remoting-destination/>”節點,就能讓這個“bean”暴露給遠端Flex調用。另外還需要配置“message-broker”,來指定BlazeDS配置文件。以上“web.xml”和“applicationContext.xml”相關配置可參考“Configuring and Using the BlazeDS MessageBroker with Spring”章節,到此,Java部分代碼就完成了。
Flex部分就兩個文件,首先是和Java相對應的“FlexGroup.as”:
[Bindable]
[RemoteClass(alias="org.rosenjiang.flex.FlexGroup")]
public class FlexGroup{
public var groupName:String;
public var member1:String;
public var member2:String;
public var member3:String;
public var member4:String;
public var member5:String;
}
}
利用[RemoteClass(alias="")]元數據映射Java端的FlexGroup對象,注意這里的成員名一定要一致,詳情可參考“Explicitly mapping ActionScript and Java objects”章節。
最后一個是“JavaFlexMapping.mxml”文件,也就是我們的主程序界面:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" backgroundColor="black" fontSize="12"
verticalAlign="middle" horizontalAlign="center" color="#000506" creationComplete="init()">
<mx:RemoteObject id="lo" destination="listDAO" result="handleResult(event)" fault="handleFault(event)">
<mx:channelSet>
<mx:ChannelSet>
<mx:channels>
<mx:AMFChannel
uri="http://127.0.0.1:8080/JavaFlexMapping/messagebroker/amf"/>
</mx:channels>
</mx:ChannelSet>
</mx:channelSet>
</mx:RemoteObject>
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.controls.Alert;
import mx.collections.ArrayCollection;
var fg:FlexGroup;
var ac:ArrayCollection;
private function init():void{
//create FlexGroup instance for remote DAO
fg = new FlexGroup();
fg.groupName = "測試組";
fg.member1 = "成員1";
fg.member2 = "成員2";
fg.member3 = "成員3";
fg.member4 = "成員4";
fg.member5 = "成員5";
}
private function handleResult(event:ResultEvent):void{
ac = ArrayCollection(event.result);
dg.dataProvider = ac;
}
private function handleFault(event:FaultEvent):void{
Alert.show(event.fault.message, "出錯了");
}
]]>
</mx:Script>
<mx:Panel height="364" width="592" title="Java Flex對象透明映射最佳實踐" horizontalAlign="center" verticalAlign="middle">
<mx:VBox>
<mx:ApplicationControlBar dock="true" width="551">
<mx:Button label="趕快點我 我就給數據" click="lo.getData(fg)"/>
</mx:ApplicationControlBar>
<mx:DataGrid height="255" width="551" id="dg">
<mx:columns>
<mx:DataGridColumn headerText="組名" dataField="groupName"/>
<mx:DataGridColumn headerText="成員一" dataField="member1"/>
<mx:DataGridColumn headerText="成員二" dataField="member2"/>
<mx:DataGridColumn headerText="成員三" dataField="member3"/>
<mx:DataGridColumn headerText="成員四" dataField="member4"/>
<mx:DataGridColumn headerText="成員五" dataField="member5"/>
</mx:columns>
</mx:DataGrid>
</mx:VBox>
</mx:Panel>
</mx:Application>
在這里引入了“RemoteObject”對象,并使用運行時注冊機制指定AMFChannel,詳情可參考“Accessing dynamic components with a Flex client application”章節。Flex程序的運行步驟是這樣的:當界面都創建完畢后,執行init()方法構造FlexGroup對象實例并填充其成員;程序進入監聽狀態,當點擊“快點我 我就給數據”的按鈕后,通過“RemoteObject”實例調用Java端的getData()方法,并傳入FlexGroup對象實例供Java端測試;最后由于注冊了異步的handleResult()方法,所以當Java端返回List對象實例后立即轉換為ArrayCollection數據類型,并填充到DataGrid進行顯示。運行截圖如下:
同時可以到Eclipse控制臺上觀察收到的數據。
到此,Java、Flex透明映射就完成了,更多信息請參考“Spring BlazeDS Integration Reference Guide”以及“BlazeDS Developer Guide”。
請注意!引用、轉貼本文應注明原作者:Rosen Jiang 以及出處: http://www.aygfsteel.com/rosen