精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-07-25
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端分为持久层(Dao层)和业务层(Service层),Flex调用的是Service层代码,使用spring mvc捕获flex的调用请求,Service再调用Dao层进行增删改查操作,最后将结果返回给Flex。
注:因为我不是UI,对Flex也才研究一个月,对页面美化和特效不了解,做出来的东西不美观,敬请大家原谅,哈哈。
①主页面
②下一页
③添加页面
④编辑页面
⑤详细页面
⑥删除操作
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" class="com.bless.logininfo.service.impl.LoginServiceImpl"> <!-- 指定当前bean被远程调用 --> <flex:remoting-destination /> <property name="loginDao" ref="loginInfoDaoBean"></property> </bean> </beans> ④最后将Java项目部署到服务器上即可。
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核心Facade private 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; //被选中的值id public 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中注册mvc facade.registerMediator(new IndexMediator(index_)); facade.registerProxy(new SearchLoginProxy()); //注册command facade.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的调用原理: ①首先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下面即可
请大家提出宝贵意见,共同进步,谢谢!
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-07-31
PureMVC,难用的要死,但是却是了解设计模式非常好的一个框架。。。。。
|
|
返回顶楼 | |
发表时间:2011-07-31
不错。支持楼主!
|
|
返回顶楼 | |
发表时间:2011-07-31
andy_ghg 写道 PureMVC,难用的要死,但是却是了解设计模式非常好的一个框架。。。。。
同感 大量的Command非常痛苦 而且为了解耦使用注册-通知的形式,造成开发维护时根本理不清mvc三层关联关系 不知道Flex还有什么优秀的mvc框架 |
|
返回顶楼 | |
发表时间:2011-07-31
helloworld这种看文档解决的东西,别浪费ITEYE的数据库了
|
|
返回顶楼 | |
发表时间:2011-08-01
asbdzxln118 写道 helloworld这种看文档解决的东西,别浪费ITEYE的数据库了
HelloWorld?看文档解决问题? 敢问你认真看过代码没,没看过别废话。 还有,用真ID示人,别用新号在那唧唧歪歪,最TM看不惯这种喷子! |
|
返回顶楼 | |
发表时间:2011-08-01
白糖_ 写道 asbdzxln118 写道 helloworld这种看文档解决的东西,别浪费ITEYE的数据库了
HelloWorld?看文档解决问题? 敢问你认真看过代码没,没看过别废话。 还有,用真ID示人,别用新号在那唧唧歪歪,最TM看不惯这种喷子! 可以鉴定这个“喷子”为无证程序员,就算有证也是没经过铁道部认证的。 |
|
返回顶楼 | |
发表时间:2011-08-01
距离实际做的solution项目,差距很大。算是入门级的吧。
|
|
返回顶楼 | |
发表时间:2011-08-01
楼上的貌似很资深,要么也写篇文章出来,让大伙瞧瞧
|
|
返回顶楼 | |
发表时间:2011-08-01
居然还有人用PureMVC!
|
|
返回顶楼 | |