Visual Web Pack 私人经验(待续)

  以下都是在netbeans5.5.1 + visual web pack5.5.1 + tomcat5.5.17开发程序时的私人经验(引用请注明出处,本人email是[email protected])。
  为了说明上的方便,Visual Web Pack将会简称为vwp。


  1、在安装了vwp之后,netbeans的帮助里就会包含vwp的帮助,如果你安装的时中文版,帮助还是中文的,非常实用,所以建议有时间看看这些帮助(方法是按F1,然后把左边的侧栏拖到最底)。


  2、NetBeans是一个以Swing为GUI的软件,所以可以非常方便地实现换肤,方法是在${NetBeans_Home}/etc/netbeans.conf中的netbeans_default_options参数后增加“--laf 外观类类名”(没有双引号),要注意的时,我曾经试过换成金属主题(即增加--laf javax.swing.plaf.metal.MetalLookAndFeel),在拖放多选框组和单选框组的时候,设计页面就会出错,所以这个功能要慎用。


  3、以前我们使用Sun JSF RI的时候,jsf页面的访问扩展名都是face,但在vwp中,还是jsp,只不过是放在${context_path}/faces下而已,所以我们可以通过这个路径直接访问。例如新建工程时默认的Page1.jsp,我们可以通过“${网址}/${context}/faces/Page1.jsp”访问。


  4、vwp的布局由于没有类似Html的table,所以非常麻烦。vwp页面的默认布局是网格布局,这个布局有点类似Dreamver的绝对定位层(Div)布局,我们一般都不推荐使用这种布局,所以,要么是使用流布局,要么是在页面增加一个什么style属性都没有的网格面板(HtmlPanelGrid)组件。我比较喜欢后者。到目前位置,我的布局一般都是使用多个网格面板嵌套来实现。网格面板组件有个columns属性,是表示这个网格面板的列的数量,通过控制这个来达到布局目的。
  不得不提的一种布局是,我们很经常会用到key-value的布局:一共有四列,第一、三列是说明,二、四列是值,就像

    姓名:张三    出生地:1984-02-29
    籍贯:佛山     民族:汉族
    性别:男      身高:190CM

  对于这种布局,我们可以通过这样实现:增加一个网格面板组件,把columns设为4,然后把12个Label属性按顺序放到这个网格面板里,然后修改Label的相应的Text即可。
  但有两个问题是需要解决:(1)key的对齐,即第一、三列的垂直对齐;(2)第二、三列之间的空隙。对于(1),可以新建一个“层叠样式表”(方法是在“web页”的resources目录右击,选择“新建”->“层叠样式表”),然后在里面增加一个styleClass,把这个styleClass的文本对齐变成右对齐,并且给一个固定的长度,例如
[code]    .commonFormKey {
      text-align: right;
      width: 150px
    }[/code]  然后把第一、三列的label的styleClass设成这个styleClass,并且在jsp页面引入这个css文件即可。对于(2),可以在“张三”这个Label处增加一个网格面板(HtmlPanelGrid)组件,并且把张三移到这个网格面板里面,最后把网格面板的宽度设为一个稍大的值即可。
  布局是一个非常复杂的话题,也是vwp的弱项,希望在以后的版本能多多加强。


  5、我们都知道jsf组件都有一个immediality(立即)属性,这个属性的作用是…,由于非常复杂,还是不说了。我们一般是把“返回”按钮的这个属性设为true。而在vwp则有了另外一个实现方法:虚拟表单。具体的使用可以查看vwp的帮助,这一块讲得还是比较详尽的。虚拟表单是vwp的特色,最好能掌握好。(需要提醒一下,立即属性为true并不是意味着跳过验证到达Action,读者最好能弄清楚)


  6、vwp有不少bug,其中有一个是如果你在左边的“概要”这里把组件拖动的比较频繁,而且用了复制、粘贴等操作,则会发现,设计界面的组件顺序跟运行时有很大的出入。解决办法是点击设计界面上面的“刷新”或者直接在jsp页面里作修改。


  7、vwp的设计时有不少地方是有缓存的,例如,你在你的页面的java文件里增加了一个getter方法,但在设计界面没有找到这个getter,那十有八九是缓存的问题,重启Netbeans就可以解决问题。根据我的个人经验,发现有些很古怪的错误,重启netbeans会搞定大部分这种错误。


  8、我们都知道jsf有六个生命周期,这个是jsf的重点,也是难点,必须要掌握。而vwp在这个基础上作了一些修改,并且向用户暴露了4个方法:init、preprocess、prerender、destroy。具体的作用可以直接看页面上的javadoc注释。


  9、以前在jsp年代,页面跳转非常容易,要么是redirect,要么是用dispatch;而在jsf,其实也有类似的页面跳转,方法是FacesContext.getCurrentInstance().getExternalContext().redirect(url),这个方法会改变vwp的页面的4个过程的流程,即直接跳到destroy,然后退出。


  10、怎么获取一个输入框的输入值?很简单,直接textField1.getText()。但如果有十几到几十个组件呢?而且非常多页面页呢,你还愿意这样逐个逐个getText吗?
  我觉得“取值”是vwp的又一弱项,竟然是需要逐个getText,真是不可想象,真有点像以前jsp的getParameter。当然,我们可以写个helper来完整这些繁琐的工作,至于具体的代码我就不贴出来了,无非就是用反射来获取属性,然后配合UIComponent的getChildre来实现。


  11、我们在修改页里,一般都会先在组件里填好原来的值,然后用户修改,提交,然后在数据库里修改。在vwp里,什么时候填原来的数据呢?有两个方法:(1)每次在prerender都填写一次数据;(2)在prerender,判断是否postback(方法是调用isPostBack()),如果不是,就填充数据。
  至于填写数据,如果组件很多,也是一件麻烦的事情,可以写一个helper来实现。方法跟上面的取值类似。不过要注意,在jsf1.1,在页面第一次请求的时候,由于页面还没有渲染,所以如果你调用UIComponent的getChildre是得不到子组件的,需要对整个页面进行反射,然后调用setter。


  12、我们应该都知道,vwp实际上是把sun的jsf ri,增加一个jsf设计时,然后增加一些其它额外组件组成的。为什么要有一个设计时呢?设计时实际上是vwp在页面的时候调用的东西。
  自定义jsf组件在网上的教程非常多,读者可以上网去搜一下,简单的jsf组件还是比较容易实现的,无非就是三个类(component、render、tag)加一个tld标签说明文件。而vwp自定义组件则复杂一些,除了要这些之外,还需要设计时,否则,就只能用、不能设计。这句话有点拗口,意思就是说,如果没有设计时,就只能在jsp里写标签,在设计界面是看不到设计效果的,但运行的时候又没有什么问题。
  至于设计时的开发,由于sun没有公布相关的资料,所以我也不知道,只知道是一些BeanInfo和配置问题。Netbeans.org好像有一个教程,可以下来看看。


  13、(重) 不知读者有没有留意,如果你使用了日历(Calendar)组件,生成的html页面的代码都会暴涨,你可以看看生成的html代码,没错,生成很多html代码,大概是每个日历组件25k左右,也就是说,如果你页面有10个这种组件,页面大小就不少于250k啦,非常惊人(我实在想不出sun怎么会作出这么糟糕的组件来)。我在好像是vwp开发人员的blog里看到说对这个组件要作出修改,不知现在改好没有。当然,我们不能干等,自己来搞定好了。
  在BlugPrint的ajax组件库(可以通过“工具”->“更新中心”来升级这个组件库)里有个popupcalendar的组件,这个日历组件生成的html代码比原来那个calendar少多了,不过有两个问题:(1)这个ajax组件库现在的版本好像是0.1,远远不能用于生产;(2)这个popupcalendar组件不能在IE里遮住Select组件(这个是IE的bug,做过设计都应该知道,但又必须要解决,毕竟用IE的人很多啊)。
  这个不能用了,怎么办?改!对原来的Calendar组件进行修改。其实这种说法不准确,应该说是对Calendar的渲染类进行修改。由于sun没有提供组件的渲染器的源代码,所以我们只能反编译这个类。类在webui.jar文件,全名是com.sun.rave.web.ui.renderer. CalendarRenderer。从这个类的反编译代码可以看到Calendar是怎么画出来的。说实话,我觉得Calendar的实现还算优雅,达到组件重用的效果,但生成的html代码实在太大了,没戏。
  我们还要找一款好的日期选择js组件,我找的是jscalendar,这款日历非常不错:跨浏览器、多皮肤、可拖动等等。
  修改现在开始,新建一个类,是com.sun.rave.web.ui.renderer. CalendarRenderer,然后把反编译的代码copy过来,删除除getStyles的所有方法,然后重写encodeEnd方法。在这个方法里画一个输入框、一个图片按钮,并且把jscalendar装进去,就搞定了。具体代码我就不贴出来了,读者自己尝试。


  14、(重) 我们都知道vwp是提供一揽子服务,即把我们以前的诸多分层都统统包揽起来了。有没有方法可以实现以前我们的分层架构呢?sure可以。
  Netbeans.org提供了一个使用hibernate的例子,其实就是分层的一个例子,不过说实话,那个例子说得乱七八糟的,我到现在还没有看完。
  闲话少说,这里我们假设是使用jsf+spring+hibernate。vwp分层其实很简单:把使用CachedRowSetDataProvider的地方都换成调用service的方法就可以了。对于数据的操作无非就是四个:CRUD,即增删改查。我们回顾一下我们系统的功能,发现其实是这样的:列表->选择记录->操作(删除或者修改)。其实就是列表那里不能直接用到service的方法,为什么?因为列表时用到Table组件,而这个组件需要一个TableDataProvider来提供数据,所以我们的工作就很简单了,找一个合适的TableDataProvider。
  在Netbeans.org的那个使用Hibernate的例子,使用的是ObjectListDataProvider,我开始也是使用这个,后来发现了一个非常严重的问题,所有继承AbstraceTableDataProvider的DataProvider都存在一个非常严重的问题:AbstraceTableDataProvider是用顺序号(即1、2、3、4….)来标识不同的记录。
  由于vwp默认是使用CachedRowSetDataProvider,数据默认保存在SessionBean里,所以每个人都有一份数据拷贝,这个问题也就不存在。但我们都知道,数据拷贝放在SessionBean里的做法弊端非常大(具体就不说了,读者可以慢慢思考)。当使用ObjectListDataProvider是,数据我们一般都是即时从数据库读出,这样,问题就出来了,假如A用户列表一些记录,如果B用户此时删掉了第二条记录,这个时候,A用户在选择第二条记录,点击删除,由于原来的第2记录已经删掉了,所以就会删掉第3条记录。
  这个问题无法解决,所以我们必须自己写一个TableDataProvider,从根本上改造这个DataProvider体系。具体代码我也不贴出来了,读者自己摸索。(提示一下吧:重写一个TableDataProvider,还要写一个RowKey,最后还要稍微改一下TableRowGroup这个组件的代码)


  15、页面间参数传递。这个其实是jsf的一个比较麻烦的问题。以前在jsp年代,直接在地址后面增加参数即可,但在jsf由于都是RequestDispatch的页面跳转,所以地址的作用已经不大。现在以例子说明问题,假如有两个页面A.jsp和B.jsp,A.jsp有两个按钮,text分别是“click A”和“click B”,都是跳转到B.jsp,请问怎么实现点击这些按钮,在B.jsp显示按钮的text呢?
  vwp提供的例子是把数据放到SessionBean里,然后跳转。(说实话,我实在有点受不了sun动不动就把数据往session里塞)
  可能很多人马上想到一个方法:request,是的,我们可以在这两个按钮的action方法里,往requestMap添加数据,然后在B.jsp取出来。不过这个方法有个弊端:就是如果在B.jsp里发生一些action操作,例如,点击了一个按钮,这时由于是另外一个request里,所以原来保存的数据就没有了。所以我们要想办法保存这个数据。
  保存的方法有很多,不过在vwp提供了一种比较方便的方法:saveData和retrieveData。如果我没有理解错的话,saveData实际上是把数据数据保存到jsf view里,我们可以从它的源代码可以看到
[code]    private static final String DATA_KEY = "com.sun.rave.web.ui.appbase.DATA";
    public void saveData(String key, Object data) {
      Map map = (Map) getFacesContext().getViewRoot().getAttributes().get(DATA_KEY);
      if (map == null) {
        map = new HashMap();
        getFacesContext().getViewRoot().getAttributes().put(DATA_KEY, map);
      }
      map.put(key, data);
    }[/code]  我们可以在B.jsp的init方法里检查request里是否存在某个key,存在,则把它saveData。数据保存后,只要不跳出这个页面,数据都可以从retrieveData里获得。
  另外一种方法,是使用HiddenField,我们在A.jsp里获取B.jsp的HiddenField,并且设置它的值,然后跳转。即在A.jsp的button的Action里执行(假如那个HiddenField的name是buttonText)
[code]    setValue("#{B.buttonText.text}", button1.getText());
    return "B";[/code]
  这样我们在B页面里就可以随时随地访问buttonText里的值。


  16、vwp其实是有两套界面的,分别对应J2EE1.4和J2EE1.5,如果你使用的是vwp5.5.1,在新建工程的时候可以发现有两个服务器可以选:tomcat5.5.17和sun Application 9,由于tomcat5.5还不支持J2EE1.4,所以你会发现当你选了tomcat5.5.17这个服务器的时候,只能选J2EE1.3和1.4,尔sun application 9就有1.5可选。这两套界面外观不同,但代码、组织架构有很多相似的地方,另外,J2EE1.5那套好像是用了标注来写组件代码的。J2EE1.4的包名是com.sun.rave.web.ui,1.5是com.sun.webui.jsf,另外,组件标签命名也不同了,1.4是 xmlns:ui="http://www.sun.com/web/ui",而1.5是 xmlns:webuijsf="http://www.sun.com/webui/webuijsf"。
  由于一般人都比较喜欢用tomcat,所以一般来说对应J2EE1.4的那套界面用得比较多。

你可能感兴趣的:(Visual Web Pack 私人经验(待续))