Flex与J2EE结合开发案例介绍
摘要:最近晚上抽出点时间写了这篇文章,关于 Flex 开发方面的语言和架构,介绍了大家都熟悉的分层结构,着重介绍视图层(flex 实现),服务控制层和领域模型层,并按照严格分层,高解耦合性并结合 Flex 技术实验了一个用户管理小模块,案例不是目的,重要的是介绍开发思想。本文第一部分介绍 Flex 相关技术以及 ActionStript3.0 语言。第二部分介绍开发实例的开发过程,代码可以下载,亮点在接口工程。由于本人 flex 经验不足,在以后的工作中会不断补充。
关键字:Flex, J2EE, 架构
1 Flex 介绍
FLEX,是Rich Internet Applications时代的牛×产物Rich Internet Applications,翻译成中文为富因特网应用程序。传统网络程序的开发是基于页面的、服务器端数据传递的模式,把网络程序的表示层建立于HTML页面之上,而HTML是适合于文本的,传统的基于页面的系统已经渐渐不能满足网络浏览者的更高的、全方位的体验要求了,这就是被Macromedia公司称之为的“体验问题”,而富因特网应用程序的出现也就是为了解决这个问题。
Flex最重要的两个技术要数AS和MXML, MXML 和 HTML 一样是标记语言,它描述了反映内容与功能的用户界面。与 HTML 不同的是,MXML 可对表示层逻辑与用户界面和服务器端数据绑定提供声明抽象。MXML 可将表示与业务逻辑的问题彻底分开,以实现最大程度地提高开发人员的生产率及应用程序的重复使用率。MXML 的开发基础是在迭代过程上,这与其他类型的 Web 应用程序文件如 HTML、JSP、ASP是相同的。开发 MXML 应用程序就象打开一个文件编辑器一样简单,只要输入一些标签、保存文件,然后在 Web 浏览器上打开文件 URL 即可。
MXML 文件同时也是普通的 XML 文件,所以可以选择多种开发环境。可以在简单文件编辑器、专用 XML 编辑器或是支持文件编辑的集成开发环境 (IDE) 中进行开发。由于 MXML 符合 W3C XML 方案的定义,您也可以使用结构化编辑,如代码着色和代码提示。
MXML和HTML间最大区别:前者定义的应用是编译的SWF文件,执行于FlashPlayer客户端中。后者是基于页面技术的应用。因此前者能提供更丰富的、动态的UI。MXML不支持Flash的某些特性,例如:时间轴。但是可以利用Flash设计组件并应用在Flex中。MXML应用可以是一个MXML文件,也可以是多个MXML组成,MXML支持MXML文件形式的自定义组件、ActionScript文件形式的自定义组件以及在Flash中建立的自定义组件。
FLEX试图通过提供一个程序员们已经熟知的工作流和编程模型,让程序员比从前更快更简单地开发动画及RIA应用。在多层式开发模型中,FLEX应用属于表现层。FLEX的语言和文件结构也试图把应用程序的逻辑从设计中分离出来
总之,flex 是未来界面开发的一个好方向,据我了解很多公司都要求员工要学习 flex 技术,可见 flex 受欢迎的程度。
2 ActionStript3.0 特性介绍
ActionScript 是针对 Adobe Flash Player 运行时环境的编程语言,它在 Flash 内容和应用程序中实现了交互性、数据处理以及其它许多功能,ActionScript 是由 Flash Player 中的 ActionScript 虚拟机 (AVM) 来执行的。ActionScript代码通常被编译器编译成“字节码格式” ,有点类似 java 的处理和运行机制。变过 C#,Java和javascript程序的朋友,肯定能在 ActionScript 中找到这些语言的影子。
2.1 内置命名空间和自定义命名空间
四个内置修饰符:
1public :对所有代码可见
2
3private: 只对类内部可见
4
5internal :只对定义所在的同一包内可见
6
7protected :对同一包以及不同包的子类可见
8
自定义命名空间:使用关键字namespace定义命名空间,声明属性和方法时,应用命名空间。
AS 代码:
1Namespace testSpace="org.blogjava.jm/ testSpace ";
2
3testSpace myfunction2():void{}
4
5 调用使用 use 关键字打开命名空间
6
7use mySpace2;
8
9myfunction2();
2.2 Local 变量特点
与java不同的是,在as3中,变量没有块级作用域(即两人大括号中间),如果在一个块内声明了一个变量,比如一个for循环内声明了一个变量,它在该代码所在整个函数内都是可访问的。
1function myFunction():void{
2
3 for(var i:int=0;i<5:i++){
4
5 var last:int = i;
6
7 }
8
9 trace(last);
10
11}
12
last 在 for 中定义但可以在 for 块外面访问到,个人觉得这样设计欠妥,尽管很方便。
2.3 Dynamic 类
Object 类本身就是动态类,当然也可以用 dynamic 关键字来声明一个类,所谓动态就是在运行时可以对类追加属性和方法。
1dynamic class Protean{}
2
3 var myProtean:Protean = new Protean();
4
5myProtean.name = "jack.wang";
6
7myProtean.password = 3;
8
9trace(myProtean. name , myProtean. password );
10
2.4 定义函数的两种方式:函数语句和函数表达式
1 //函数语句声明
2
3Function fun1():void{}
4
5 //函数表达式声明
6
7var fun2:Function = function():void{}
8
9函数表达式声明的函数是不能够被垃圾回收的,必须显示的调用 delete 命令。
10
11var t:Test = new Test(); //t是动态类Test的一个实例
12
13t.funt = function(){};//把一个函数声明为t的一个属性
14
15delete t.funt; //删除刚才新增的函数,我们刚才声明的函数也被回收了
16
2.5 在as3中也有arguments对象,功能基本上和javascript中的一样。
arguments 对象是一个数组,其中包括传递给函数的所有参数,arguments.length 属性报告传递给函数的参数数量,(避免将 "arguments" 字符串作为参数名,因为它将遮蔽 arguments 对象)
1 function traceArgArray(x:int):void
2
3{
4
5 for (var i:uint = 0; i < arguments.length; i++)
6
7 {
8
9 trace(arguments[i]);
10
11 }
12
13}
14
15traceArgArray(1, 2, 3);
16
17// 输出:
18
19// 1
20
21// 2
22
23// 3
24
当然也可以用类似 java 的形式
function traceArgArray(x: int, args)
2.6 在as3中允许定义同名的静态属性和实例属性
1class StaticTest
2
3{
4
5 static var message:String = "static variable";
6
7 var message:String = "instance variable";
8
9}
10
11// 在脚本中
12
13var myST:StaticTest = new StaticTest();
14
15trace(StaticTest.message); // 输出:静态变量
16
17trace(myST.message); // 输出:实例变量
18
2.7 在as中有一个叫做set和get存取器的东西,类似JavaBean,但又有所不同。
class GetSe
t{
private var privateProperty:String;
public function get publicAccess():String
{
return privateProperty;
}
public function set publicAccess(setValue:String):void
{
privateProperty = setValue;
}
}
3 案例介绍
案例是超级简单的,开发Web 应用的,不管你是基于 J2EE,.net 平台还是其他的 PHP 等等都很熟悉分层架构,其中五层是最熟悉不过的了,一般分为表示层,控制层,服务层,模型层和数据层,有的公司干脆就把服务层和模型层合并起来和控制层合并在一起,基于分层的开发可以横向也可以纵向,由于引入 flex 技术,因为并不是所有人都懂得 flex 开发方式,尽管他和传统的 Swing, SWT,GWT开发很相近 ,不过这样需求分析时可以构建可演进的原型系统,界面也很 RIA。
人们常提面向接口编程于是经常看到 IUser user=new User();这样的代码,似乎就是在用接口,但是 new 的动作还是有依赖,于是人们用一些设计模式比如 Factory, Proxy,反射等等模式,后来又有了更好的依赖注入,似乎很完美了,可人们又陷进了过度设计,其实接口应用比直接用原类要多耗费几个机械指令 User user=new User(),而且在一个内聚的组件中尽量用对象直接连接,该分开的时候我们去用接口分,接口的目的是为了抽象,抽象的目的是为了解耦。
本开发为了把模型层和服务层完全分开,引入接口工程,也就是单独开发一个工程来设计接口,这个工程有设计师开发。
用户接口:
package org.blogjava.model;
/** *//**
*
*/
publicinterface IUser {
long getId();
void setId(long id);
void setUserName(String name);
String getUserName();
void setPassword(String password);
String getPassword();
void setEmail(String email);
String getEmail();
void setAddress(String address);
String getAddress();
}
工厂接口:
package org.blogjava.dao;
import java.util.List;
import org.blogjava.model.IUser;
import org.blogjava.util.InstanceUtils;
import org.blogjava.util.PropertiesUtils;
/** *//**
*
*/
publicinterface DAOFactory {
publicfinal String IMPL_NAME = PropertiesUtils.getParam(
"/DaoFactoryImpl.properties", "DAOFactoryImpl");
publicfinal DAOFactory factory = (DAOFactory) InstanceUtils
.newInstance(IMPL_NAME);
boolean checkUserValid(String userName, String password);
IUser getUserById(String id);
IUser createUser(IUser user);
boolean deleteUserById(String id);
boolean updateUser(IUser user);
List<IUser> getAllUsers();
}
这两个类在单独的工程里开发,以便于模型和服务层同步开发。
在服务层不能对外暴露 IUser 接口,而他也要接受数据,这里用的是 UserVO 对象来封装数据,服务层不操作模型层的任何实现类,这样这两层之间完全解耦。
package org.blogjava.vo;
/** *//**
*
*/
publicclass UserVO {
publiclongid;
public String userName;
public String password;
public String email;
public String address;
public UserVO() {
}
}
Flex 开发和 HTML开发有很大的不同,客户端和服务端交互的不是 HTML 而是 XML,所以在服务器端的 Servlet 就应该输出 XML 流到客户端
判断登录:
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
String userName = request.getParameter("userName");
String password = request.getParameter("password");
String sucess = "<?xml version=""1.0"" encoding=""utf-8""?><result><msg>sucess</msg></result>";
String unsucess = "<?xml version=""1.0"" encoding=""utf-8""?><result><msg>unsucess</msg></result>";
response.setContentType("text/xml");
try {
if (userName == null || "".equals(userName)) {
response.getOutputStream().write(unsucess.getBytes());
returnnull;
}
if (password == null || "".equals(password)) {
response.getOutputStream().write(unsucess.getBytes());
returnnull;
}
IUserService service = new IUserServiceImpl();
if (service.isUserValid(userName, password)) {
response.getOutputStream().write(sucess.getBytes());
} else {
response.getOutputStream().write(unsucess.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
returnnull;
}
得到所有的用户:
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
IUserService service = new IUserServiceImpl();
List<UserVO> users = service.getAllUsers();
try {
StringBuffer returnStr = new StringBuffer(
"<?xml version=""1.0"" encoding=""utf-8""?>");
returnStr.append("<users>");
for (UserVO user : users) {
returnStr.append("<user>");
returnStr.append("<id>" + user.id + "</id>");
returnStr.append("<userName>" + user.userName + "</userName>");
returnStr.append("<password>" + user.password + "</password>");
returnStr.append("<email>" + user.email + "</email>");
returnStr.append("<address>" + user.address + "</address>");
returnStr.append("</user>");
}
returnStr.append("</users>");
response.getOutputStream().write(returnStr.toString().getBytes());
System.out.println(returnStr.toString());
} catch (IOException e) {
e.printStackTrace();
}
returnnull;
}
最后 Flex 也是一个单独的工程,通过 HTTPService 和 Web 服务器端交互数据
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="" layout="absolute" backgroundGradientAlphas="[0.72, 1.0]" backgroundGradientColors="[#FFFFFF, #FFFFFF]">
<mx:HTTPService
showBusyCursor="true"
id="loginSrv"
result="doResult();"
method="GET"
url="">
<mx:request>
<userName>
{txtname.text}
</userName>
<password>
{txtpwd.text}
</password>
</mx:request>
</mx:HTTPService>
<mx:HTTPService
showBusyCursor="true"
id="loadUsers"
method="GET" result="users = event.result.users.user"
url="">
</mx:HTTPService>
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.utils.ObjectUtil;
import mx.collections.ArrayCollection;
[Bindable]
var users:ArrayCollection=new ArrayCollection();
internal function doResult():void
{
var returnValue:String=loginSrv.lastResult.result.msg;
if(returnValue=="sucess")
{
login.visible=false;
loadUsers.send();
userList.visible=true;
}
else
{
Alert.show("登录失败","提示信息",Alert.OK,this,null,null,Alert.YES);
}
}
private function index_sortCompareFunc(itemA:Object, itemB:Object):int {
// Make sure itemA has an "index" property.
if (!itemA.hasOwnProperty("id")) {
itemA.index = null;
}
// Make sure itemB has an "index" property.
if (!itemB.hasOwnProperty("id")) {
itemB.index = null;
}
// Perform a numeric sort.
return ObjectUtil.numericCompare(itemA.index, itemB.index);
}
]]>
</mx:Script>
<mx:Panel id="login" x="285.5" y="127" width="341" height="238" layout="absolute" fontFamily="Arial" fontSize="13" color="#3C280B" borderStyle="none" borderColor="#CC0B0B" cornerRadius="10" alpha="0.64">
<mx:TextInput id="txtname" x="101" y="34" width="172" displayAsPassword="true" editable="true" enabled="true"/>
<mx:Button x="92" y="130" label="登录" labelPlacement="top" enabled="true" click="loginSrv.send();"/>
<mx:Button x="183" y="130" label="取消"/>
<mx:TextInput id="txtpwd" x="101" y="75" width="172" displayAsPassword="true" editable="true" enabled="true"/>
<mx:Label x="44" y="36" text="帐号:" width="49" height="20" enabled="true"/>
<mx:Label x="44" y="77" text="密码:" width="49" height="20" enabled="true"/>
</mx:Panel>
<mx:Panel id="userList" x="181.5" y="98" width="576" height="311" layout="absolute" visible="false">
<mx:DataGrid x="0" y="0" width="556" height="271" sortableColumns="true" dataProvider="{users}" editable="true" enabled="true">
<mx:columns>
<mx:DataGridColumn headerText="编号" dataField="id" />
<mx:DataGridColumn headerText="姓名" dataField="userName" />
<mx:DataGridColumn headerText="密码" dataField="password"/>
<mx:DataGridColumn headerText="邮件" dataField="email"/>
<mx:DataGridColumn headerText="地址" dataField="address"/>
</mx:columns>
</mx:DataGrid>
</mx:Panel>
</mx:Application>
4 小结
在写这篇文章的时候已经是深夜了,可能有很多地方表述有问题,也可能有的思想没有表述清楚,欢迎大家讨论,就先写到这里,有时间我会继续补上一些内容。