Tapestry4.1.5 tree组件问题的解决及页面缓存带来的问题

defencez 2008-09-27
    Tapestry使用缓存来极大地提高效率,根据使用情况,使用缓存能提高大约50%
(甚至更高,当然,编程时应具有比较良好的风格,且对Tapestry的缓存机制比较了解),首先,当一个页第一次被访问,该页以对象方式生成后,其中所有属性首次初始化,页面访问完毕后,被放入页面cache pool中,当再次访问该页时,不会再生成该页面对象,而是直接从缓存中拿出使用,极大提高效率。第二,页面中属性不会再次生成,节省了对象创建的开销。但同时也带来多用户操作时,属性值不更新的问题。

缓存带来的两方面问题

例如,在树组件的使用中,其中有treeModel属性,该属性为构建整个树的入口点,是一个开销比较大的对象,特别是树节点值从数据库提取的情况下,代码如下

   public ITreeModel getTreeModel() {
    if(treeModel == null){
       initTree();//构造树的数据都来自于数据库
    }
   return treeModel;
}

当关闭Tapestry page cache时,只要页面被再次访问(刷新),则treeModel被清空,则每次都必将执行initTree()的动作,这会反复读取数据库,造成效率的极大降低。其次,B/S架构下的无状态连接协议,首次点击树中的节点(树不是由JavaScript构建,状态更新由服务端完成),向服务端有提交该点击请求,则更新当前页面,展开或收缩节点动作无法完成,因为二次提交后,执行上面的代码,此时treeModel为空,树将被重新构建,先前树状态丢失。
当开启Tapestry page cache时,若treeModel不为空,则initTree()不会被执行,且二次提交后(指点击树节点提交点击请求),页面treeModel属性保持了上一次的状态,根据此次请求展开或收缩节点动作得以执行,可明显见到页面访问快了很多(大概快了75%)。换另一台机器访问该页面,却发现两台机器看到的树的状态是一模一样的,另一台机器本该看到一棵刚初始化完毕的树。

解决
    因为Tapestry应用应该开启页面缓存机制,以提高应用的性能,因此,只说在页面缓存开启的情况下的解决方案,其实以下的两种方案都可以用在未开启页面缓存的情况。

自建一Session对象

在Tapestry中,可自建一个能持久化的对象(implements Serializable,即可),把需要临时存储的放入该对象。如下面的代码
if (treeModel == null) {
     if(this.getApplicationSession().getTreeModel() != null){
     treeModel = this.getApplicationSession().getTreeModel();
        treeDataModel = this.getApplicationSession().getTreeDataModel();
      }else{
            initTree();
this.getApplicationSession().setTreeModel(treeModel);               this.getApplicationSession().setTreeDataModel(treeDataModel);
}
    }else{
        this.getApplicationSession().setTreeModel(treeModel);
        this.getApplicationSession().setTreeDataModel(treeDataModel);
}

在Hivemind中的配置片断
  <contribution configuration-id="tapestry.state.ApplicationObjects">
    <state-object name="applicationSession" scope="session">
      <create-instance class="com.soa.system.ApplicationSession"/>
    </state-object>
  </contribution>

代码中,ApplicationSession就是一执行了序列化接口的普通对象。

利用Session对象
每个页面访问,都会有一个HTTP的Session对象,那么在这个Session对象中存储treeModel,判断当前的treeModel的情况,每次都更新Session对象中存储的treeModel,这样就可以既利用了缓存,又能使不同用户视图所看到的树状态不同,且不会造成相同,就可判断访问是不是来自于同一个Session,这就可以解决不同用户看到同一种树状态的问题。
以上两种做法其实是等同的,虽然解决了问题,但有缺陷,当用户离开了这个页面,转入其他页面之后再回到此页面,此页面的树状态仍然是用户离开时的样子,就是说,清空对象中的treeModel属性的时机不好控制。
以下是代码片断

首先注入HttpRequest对象
@InjectObject("service:tapestry.globals.HttpServletRequest")
public abstract HttpServletRequest getRequest();

然后:
    if(getRequest().getSession().getAttribute("treeModel") == null){
    initTree();
    getRequest().getSession().setAttribute("treeModel",treeModel);
    getRequest().getSession().setAttribute("treeDataModel",treeDataModel);
    }else{
    treeModel = (ITreeModel)getRequest().getSession().getAttribute("treeModel");
    treeDataModel = (SimpleTreeDataModel)getRequest().getSession().getAttribute("treeDataModel");
    }
    return treeModel;
注意:在存储treemodel时,treeDataModel也需一并存储

所有代码是本人在做一Tapestry工程中的一段代码,因代码太长,此处只贴出有关的代码,不知这里格式怎么整理,显得很乱,大家将就看看吧。

这两种方式在t4.1.5及t4.1.6下面都运行正常。
defencez 2008-09-27
对上面的补充:

那属性是否是重新初始化,要看用户在何处定义的,如在页面规范文件中定义,则T每次都自动初始化,若在页面类文件中定义的,需要手工初始化。

重载initialize()方法,在该方法中做属性初始化,则T每次会自动调用该方法。
zbyidingxing 2008-10-27
我刚接触tapestry 我现在项目用的是4.0 也是做了一颗树 请问如何在树结点上挂上url连接 可以实现跳转
谢谢了
defencez 2008-10-28
T是实现了PME模型的,点击树节点后,可以得到节点的值,也就是节点选择的事件,在页面类中做一getSelectedNode的方法(方法你自行定义)在该方法中实现跳转即可
Global site tag (gtag.js) - Google Analytics