Flex实战-puremvc+java(spring+hibernate)实现增删改查操作
Blaseds是一款开源免费的插件,主要作用是实现flex调用java代码;
puremvc是一款针对flex的开源免费的mvc框架,主要作用是实现Flex代码解耦;
Hibernate框架是java持久层经典框架,帮助程序员更方便安全地访问数据库;
spring是java非常流行的框架,主要作用是管理java对象,其强大的低耦合机制能够与许多框架进行无缝整合(比如前面提到的Blaseds和Hibernate)。
?
实现机制本实例实现用户管理的增删改查操作,前端完全使用flash展示,后台使用java服务器与mysql数据库交互。
Flex端使用puremvc进行解耦,Mediator监听页面事件和页面数据存取,Command处理页面复杂操作并调用Proxy方法,Proxy实现Java代码调用(使用RemoteObject的形式)。
Java端分为持久层—ao层)和业务层(Service层),Flex调用的是Service层代码,使用spring mvc捕获flex的调用请求,Service再调用Dao层进行增删改查操作,最后将结果返回给Flex。
?
效果展示注:因为我不是UI,对Flex也才研究一个月,对页面美化和特效不了解,做出来的东西不美观,敬请大家原谅,哈哈。①主页面
?
?②下一页
?
?③添加页面
?
?④编辑页面
?
?⑤详细页面
?
?⑥删除操作
?
Java端实现Java端主要使用spring和Hibernate实现Dao层和Service层代码,具体spring和Hibernate配置网上有很多,在这就不赘述了。
关于Blaseds与spring整合的例子也很多,大家可以到我的博文中参考一下:http://blessht.iteye.com/admin/blogs/1131148。
?
①下面看Flex远程调用Service的接口定义:
public interface LoginService {//新增public void insertLoginInfo(Logininfo login);//删除public void deleteLoginInfo(int id);//修改public void updateLoginInfo(Logininfo login);//详细public LoginInfoDto getLoginInfo(int id);//分页查询用户信息(用于显示到表格中)public List<LoginTableDto> findLoginInfo(int pageNo,int pageSize);//获取用户信息表的总数据public long getLoginInfoTotalCounts();//进入编辑页面时调用的方法public Logininfo getLogin(int id);//暂时无用public List<FlexComboBoxDto> getSexList();}
?
?②看比较关键的web.xml配置:
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"><display-name>ssh2model</display-name><description>ssh2model Application</description><!-- spring mvc --><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></param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>Spring MVC Dispatcher Servlet</servlet-name><url-pattern>/messagebroker/*</url-pattern></servlet-mapping><!-- 著名 Character Encoding filter --><filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param></filter><!-- spring IOC --><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:com/bless/base/config/spring/springApplicationContext.xml,classpath:com/bless/*/config/spring/spring-*-*.xml </param-value></context-param><!-- blaseds监听器:用于获取服务器内置对象(request,application等) --><listener><listener-class>flex.messaging.HttpFlexSession</listener-class></listener><!--Spring ApplicationContext 载入 ,必须--><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><!-- Spring 刷新Introspector防止内存泄露 --><listener><listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class></listener><!-- session超时定义,单位为分钟 --><session-config><session-timeout>20</session-timeout></session-config><!--添加远程支持,用于远程数据服务--><servlet><display-name>RDSDispatchServlet</display-name><servlet-name>RDSDispatchServlet</servlet-name><servlet-class>flex.rds.server.servlet.FrontEndServlet</servlet-class><init-param><param-name>useAppserverSecurity</param-name><param-value>false</param-value></init-param><init-param><param-name>messageBrokerId</param-name><param-value>_messageBroker</param-value></init-param><load-on-startup>10</load-on-startup></servlet><servlet-mapping id="RDS_DISPATCH_MAPPING"><servlet-name>RDSDispatchServlet</servlet-name><url-pattern>/CFIDE/main/ide.cfm</url-pattern></servlet-mapping></web-app>
?
③最后看service的IOC配置文件:
<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-3.0.xsd http://www.springframework.org/schema/flex http://www.springframework.org/schema/flex/spring-flex-1.0.xsd"> <!-- 为了把请求路由给 MessageBroker,添加以下 tag--><flex:message-broker /><bean id="loginServiceBean" ref="loginInfoDaoBean"></property></bean></beans>?
④最后将Java项目部署到服务器上即可。
?
Flex端实现Flex端因为使用了puremvc,关于puremvc我在这不做过多介绍,因为我也是新手,可能很多东西写的都是错误,在以后对它有更深认识之后我会与大家分享使用经验。
?
整个项目的包结构如下图所示:
上图*.mxml就是flex的页面展示了,功能与jsp和html相似,也是由页面标签、事件等组成。mxml可以像纯jsp一样,将所有代码写在里面,但是这样会造成项目难以维护,所以为了尽量减少耦合度,我在mxml中基本没有写ActionScript代码,而是交给puremvc的Mediator处理。
下面以index.mxml为例,我在index.mxml中定义了一些变量,方便Mediator赋值,也就是说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" minWidth="955" minHeight="600" creationComplete="facade.init_index(this)"><!--整体布局--><s:layout><s:VerticalLayout horizontalAlign="center" verticalAlign="middle"/></s:layout><!--ActionScript代码块--><fx:Script><![CDATA[import mx.collections.ArrayCollection;//定义puremvc核心Facadeprivate var facade:ApplicatioinFacade = ApplicatioinFacade.getInstance();//总条数[Bindable]public var totals:int = 0;//当前页数[Bindable]public var pageNo:int = 1;//一页显示条数[Bindable]public var pageSize:int = 6;//表格中的结果集[Bindable]public var logintablelist:ArrayCollection;//被选中的值idpublic var selectId:int = 0;protected function table_loginInfo_clickHandler(event:MouseEvent):void{if(table_loginInfo.selectedItem != null){selectId = table_loginInfo.selectedItem.id;}}]]></fx:Script><!--<s:HGroup width="70%" height="30" verticalAlign="middle" horizontalAlign="center"><s:Label text="用户名:"/><s:TextInput id="txt_loginCode"/><s:Label text="姓名:"/><s:TextInput id="txt_name"/><s:Label text="性别:"/><s:ComboBox width="100" id="txt_sex"/><s:Button label="检索" id="btn_search"/><s:Button label="重置" id="btn_reset"/></s:HGroup>--><!--操作区--><s:HGroup width="70%" height="30" verticalAlign="middle"><mx:LinkButton label="刷新" id="refresh"/><mx:LinkButton label="添加" id="insert"/><mx:LinkButton label="编辑" enabled="{table_loginInfo.selectedIndex==-1?false:true}" id="edit"/><mx:LinkButton label="详情" enabled="{table_loginInfo.selectedIndex==-1?false:true}" id="detail"/><mx:LinkButton label="删除" enabled="{table_loginInfo.selectedIndex==-1?false:true}" id="link_delete"/></s:HGroup><!--表格区--><mx:DataGrid width="70%" id="table_loginInfo" dataProvider="{logintablelist}" click="table_loginInfo_clickHandler(event)"><mx:columns><mx:DataGridColumn headerText="用户名" dataField="loginCode"/><mx:DataGridColumn headerText="姓名" dataField="name"/><mx:DataGridColumn headerText="性别" dataField="sex"/><mx:DataGridColumn headerText="联系电话" dataField="phone"/><mx:DataGridColumn headerText="电子邮箱" dataField="email"/></mx:columns></mx:DataGrid><s:HGroup width="70%" height="30" verticalAlign="middle"><s:Label text="总共"/><s:Label text="{totals}"/><s:Label text="条数据"/><s:Label text=" 第"/><s:NumericStepper id="curPage" minimum="1" maximum="{(totals%pageSize)!=0?((uint)(totals/pageSize)+1):(totals/pageSize)}" value="{pageNo}"/><s:Label text="页"/><mx:LinkButton label="上一页" id="befor_page" enabled="{(pageNo==1)?false:true}"/><mx:LinkButton label="下一页" id="after_page" enabled="{(totals>((pageNo-1)*pageSize+(logintablelist.length)))?true:false}"/></s:HGroup></s:Application>
?
大家可以看到index.mxml中本应有很多事件的,不然怎么能实现增删改查操作,其实上面所有事件都写到了这里:
public class IndexMediator extends Mediator{public static const NAME:String = "IndexMediator";public function IndexMediator(viewComponent:Object=null){super(IndexMediator.NAME, viewComponent);var indexApp:index = viewComponent as index;//刷新indexApp.refresh.addEventListener(MouseEvent.CLICK,function click():void{sendNotification(LoadLoginCommand.NAME,indexApp);});//上下页选择事件indexApp.curPage.addEventListener(Event.CHANGE,function change():void{loadTable(indexApp.curPage.value);});//上一页事件indexApp.befor_page.addEventListener(MouseEvent.CLICK,function click():void{loadTable(indexApp.pageNo - 1);});//下一页事件indexApp.after_page.addEventListener(MouseEvent.CLICK,function click():void{loadTable(indexApp.pageNo + 1);});//打开添加操作事件indexApp.insert.addEventListener(MouseEvent.CLICK,function click():void{sendNotification(LoginAddOpenCommand.NAME,indexApp);});//打开编辑操作事件indexApp.edit.addEventListener(MouseEvent.CLICK,function click():void{sendNotification(LoginEditCommand.NAME,indexApp);});//打开详细操作事件indexApp.detail.addEventListener(MouseEvent.CLICK,function click():void{sendNotification(LoginDetailCommand.NAME,indexApp);});//执行删除操作事件indexApp.link_delete.addEventListener(MouseEvent.CLICK,function click():void{Alert.show("你确定该条数据吗?","删除提示",Alert.OK|Alert.NO,indexApp,function alert(e:CloseEvent):void{//如果点击Cancel则不执行任何操作,否则执行删除操作if(e.detail == Alert.OK){sendNotification(LoginDeleteCommand.NAME,indexApp);}});});}public function get indexApp():index{return viewComponent as index;}//列举监听事件override public function listNotificationInterests() : Array{ return [ SearchLoginProxy.findLoginInfo,SearchLoginProxy.getLoginInfoTotalCounts,SearchLoginProxy.deleteLoginInfo]; }//处理监听事件override public function handleNotification( note : INotification ) : void{switch (note.getName()){case SearchLoginProxy.findLoginInfo:indexApp.logintablelist = note.getBody() as ArrayCollection;break;case SearchLoginProxy.getLoginInfoTotalCounts:indexApp.totals = note.getBody() as int;break;case SearchLoginProxy.deleteLoginInfo:sendNotification(LoadLoginCommand.NAME,indexApp);break;default:break;}}private function loadTable(pageNo:int):void{indexApp.pageNo = pageNo;sendNotification(LoadLoginCommand.NAME,indexApp);}}?
那么上面IndexMediator类的构造方法就是关键了,构造方法必须传入index.mxml的实现对象,其实实现步骤很简单:
①index.mxml页面启动是触发creationComplete事件,该事件调用facade的init_index方法并且将当前对象作为参数传过去
②在facade类中注册一个名叫InitCommand的类,通过sendNotification向InitCommand发送通知
public class ApplicatioinFacade extends Facade{public static const INIT:String = "init";//得到ApplicationFacade单例的工厂方法public static function getInstance() : ApplicatioinFacade{if ( instance == null ) instance = new ApplicatioinFacade( );return instance as ApplicatioinFacade;}//注册Command,建立Command与Notification之间的映射override protected function initializeController( ) : void{super.initializeController();registerCommand(InitCommand.NAME,InitCommand);}public function init_index(index_:index):void{sendNotification(InitCommand.NAME,index_);}}?
③InitCommand接到通知后运行execute方法,execute方式内完成MVC的注册(当然也包括IndexMediator的注册)。其实到这IndexMediator就已经开始监听index.mxml的事件了。
public class InitCommand extends SimpleCommand{public static const NAME:String = "InitCommand";override public function execute(notification:INotification):void{var index_:index = notification.getBody() as index;//向facade中注册mvcfacade.registerMediator(new IndexMediator(index_));facade.registerProxy(new SearchLoginProxy());//注册commandfacade.registerCommand(LoadLoginCommand.NAME,LoadLoginCommand);facade.registerCommand(LoginDetailCommand.NAME,LoginDetailCommand);facade.registerCommand(LoginDeleteCommand.NAME,LoginDeleteCommand);facade.registerCommand(LoginEditCommand.NAME,LoginEditCommand);facade.registerCommand(LoginAddReturnCommand.NAME,LoginAddReturnCommand);facade.registerCommand(LoginEditSubmitCommand.NAME,LoginEditSubmitCommand);facade.registerCommand(LoginAddOpenCommand.NAME,LoginAddOpenCommand);facade.registerCommand(LoginEditCloseCommand.NAME,LoginEditCloseCommand);//发送通知初始化页面sendNotification(LoadLoginCommand.NAME,index_);}}
?
puremvc运行流程简介以打开新增页面为例,我简单介绍一下puremvc的调用原理:
①首先IndexMediator负责index.mxml的事件监听,当用户点击“新增”链接时向LoginAddOpenCommand发送通知,并且将index.mxml对象传给该command使用:
//打开添加操作事件indexApp.insert.addEventListener(MouseEvent.CLICK,function click():void{sendNotification(LoginAddOpenCommand.NAME,indexApp);});
?②发送通知后,facade调用LoginAddOpenCommand的execute方法,该方法创建一个insert.mxml对象,同时创建LoginAddMediator(该Mediator用于监听insert.mxml页面),并且以模式弹出窗的形式显示在界面中,这时需要初始化“性别”下拉列表,所以必须调用SearchLoginProxy的getSexList方法:
public class LoginAddOpenCommand extends SimpleCommand{public static const NAME:String = "LoginAddOpenCommand";override public function execute(notification:INotification):void{//新建添加窗口var i:insert = new insert();PopUpManager.addPopUp(i,notification.getBody() as index,true);PopUpManager.centerPopUp(i);facade.registerMediator(new LoginAddMediator(i));(facade.retrieveProxy(SearchLoginProxy.NAME) as SearchLoginProxy).getSexList();}}
?③SearchLoginProxy的getSexList方法主要封装“性别”下拉列表的数据,封装完成后会发出一个通知
//查询性别列表public static const getSexList:String ="getSexList";public function getSexList():void{var list:ArrayCollection = new ArrayCollection([{label:"保密",data:0},{label:"男",data:1},{label:"女",data:2}]);sendNotification(SearchLoginProxy.getSexList,list);}
? ④在LoginAddMediator中会注册并收听到Proxy发过来的通知,最后通过LoginAddMediator将值赋给insert.mxml的combox组件:
//列举监听事件override public function listNotificationInterests() : Array{ return [SearchLoginProxy.insertLoginInfo,SearchLoginProxy.getSexList]; }//处理监听事件override public function handleNotification( note : INotification ) : void{switch (note.getName()){case SearchLoginProxy.insertLoginInfo:(insertApp.parentDocument as index).pageNo = 1;sendNotification(LoginAddReturnCommand.NAME,insertApp);break;case SearchLoginProxy.getSexList://给“性别”combox赋初始值insertApp.sexList = note.getBody() as ArrayCollection;insertApp.cb_sex.selectedIndex = 0;default :break;}}
?
尚未实现的功能Java端并发和异常处理,Flex端异常处理、表单验证等等...项目环境eclipse3.5、mysql5、Flashbuilder4
附件“flex_sql.rar”包含flex源码和数据库脚本附件“ssh2model.rar”包含java源码附件“jar.rar”包含java项目的jar文件,解压后丢到ssh2model\WebContent\WEB-INF\lib下面即可
请大家提出宝贵意见,共同进步,谢谢!
1 楼 ltian 2011-08-07 PureMVC是用来实现UI组件化,解耦用的。我是不用这个的东西,祝你们好运。 2 楼 念123 2011-10-10 学习了啊, 可是我有个问题 请教下, 性别选择框 里的数据,如果从数据库来得。怎么办呢,我也在做一个类似的项目,可是我初始化加载时 老是有问题。 能否 帮忙解答呢?