Chinaunix首页 | 论坛 | 博客
  • 博客访问: 482775
  • 博文数量: 1496
  • 博客积分: 79800
  • 博客等级: 大将
  • 技术积分: 9940
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-09 13:22
文章分类

全部博文(1496)

文章存档

2011年(1)

2008年(1495)

我的朋友

分类:

2008-09-09 13:31:19

    Apache MyFaces项目包含了几个关于Server技术的子项目。如果需要了解更多关于Server Faces的知识,请参考MyFaces对于JSF的介绍。
    Apache MyFaces项目提供了以下的功能:
    JavaServer Faces的实现(MyFaces API和MyFaces Impl模块)
    用于构建JSF相关的web应用程序的组件库,例如MyFaces Tomahawk、MyFaces Trinidad、MyFaces Tobago。
    JavaServer Faces扩展包,例如MyFaces Orchestra。
    与其他技术和标准的集成模块。(用于开发移动客户端,在本文和后续文章中对这部分功能不进行介绍。)
    可以在如下位置和MyFaces Impl及其各个子项目的发布包及样例程序。MyFaces API

    在JSF的子项目中,Core子项目视JSF规范的一个实现,其他的项目实现了相关的规范,例如Portlet Bridge,或者为其他的JSF实现添加了扩展,注意,不仅仅是MyFaces Core。同时,MyFaces的扩展,例如Tomahawk,可以和任意的JSF实现协同工作,例如Sun Reference Inplementation。
    MyFaces Core1.1.x可以在Tomcat 5.5下工作,但是MyFaces Core 1.2.x要求 Tomcat 6.0环境。
    MyFaces项目不依赖于其他的项目。它是一个独立的程序。

    JSF FAQ
    1.     javax.faces.STATE_SAVING_METHOD的值为client和server时的区别?
    简而言之,server端状态降将UI组件的持有的信息在HTTP Session中保存,而client状态将UI组件持有的信息保存在返回给用户的页面的隐藏域(hidden field)。
    client端状态对于非常大数量的用户来说比较实用,因为不需要在端保存用户的状态,从而节省了内存。但是缺点是在网络中为每个请求传送了更多的数据。
    即使选择的是client端状态,任何一个session-scoped实体仍然存在于http session中,这个标记只是影响JSF实现将UI组件持有的数据存放在什么位置。
    任何实现了StateHolder的组件,必须实现saveState(FacesContext)和restoreState(FacesContext, Object)方法,来帮助JavaServer Faces Implementation来保存和恢复跨越多个请求的组件的状态。
    为了设置值,必须实现saveState方法。这个方法在render响应阶段被调用,在这期间响应的状态被设置,用来处理后需的请求。下面是MapComponent组件的该方法的实现:
    public Object saveState(FacesContext context) {
        Object values[] = new Object[2];
        values[0] = super.saveState(context);
        values[1] = current;
        return (values);
    }
    该方法初始化了一个数组,用来持有保存了的状态,然后保存所有与MapComponent关联的状态的所有内容。
    实现StateHolder的组件必须提供restoreState方法,该方法用来恢复保存在saveState方法中保存的组件的状态。restoreState方法在恢复视图阶段被调用,在此期间,JavaServer Faces Implementation会检查是否在最后render response阶段保存了状态,并判断是否需要恢复,以便进行下次的后退。下面是MapComponent组件的restoreState方法。
    public void restoreState(FacesContext context, Object state) {
        Object values[] = (Object[]) state;
         super.restoreState(context, values[0]);
        current = (String) values[1];
    }
    该方法的参数为FacesContext和Object实例,表示组件状态的数组。这个方法将Object数组中保存的数据进行设置。
    当在自定义的组件类中实现这些方法时,确保在部署描述符中指定了需要将这些状态保存在哪里,即client还是server。如果状态时保存在client,整个视图的状态都被提供给页面的一个hidden field。
    可以通过设置javax.faces.STATE_SAVING_METHOD上下文参数(context-parameter)。
    2.     浏览器总是显示一个到上一个页面的链接?
    默认情况下,JSF在内部使用forward操作来在页面之间导航。
    所以当一个用户首先访问页面A,然后取回一些内容。这些内容会被包装在表单中,表单以页面A作为提交的地址。
    当用户接着执行操作,提交页面,这时JSF收到post请求,回复页面A的视图,并执行postback处理。作为postback处理的一部分,页面A的逻辑可能通知JSF框架应该显示页面B。
    默认情况下,JSF会执行一个内部forward到页面B,使页面B被取回,作为用户提交表单的结果。
    这时用户在屏幕上看到页面B。但是浏览器只知道它将数据发送给了页面A,所以,浏览器导航栏上显示的URL是页面A的URL,虽然显示的内容是页面B。不幸的是,HTTP/HTML不能使浏览器知道应该显示页面B的URL。因此,作为JSF的默认行为,浏览器的URL经常是实际显示内容的页面的上一页。
    不仅仅对于用户来说是奇怪的,同时加入收藏夹的时候也比较困难,注意,收藏夹的位置一般来说是没有关系的。因为JSF应用程序通常是可交互的,并且是状态丰富的。
    另外一个缺点是在JSF视图中使用关联链接是不的。(例如样式表的相对路径或者图标)。当浏览器提交内容到A,并获取数据共页面B使用时,在返回页面中的relative links都会被作为相对于它(浏览器)所知道的最后的url。所以第一次查看页面B时,所有相对链接都是相对于页面A的,如果A和页面B不在一个目录中,那么就会出现问题。
    这个问题的一个解决办法就是使用标记来定义导航规则。这样,会使用http redirect命令来告诉浏览器获取新页面,从而取代执行内部的forward。 但是,这样做效率比较低,因为浏览器需要使用第二次请求来获取它的内容,而不是在针对初始的post请求,在响应时立即获取它的内容。另外一个重要的问题就是,获取页面B的内容是在一个单独的请求中,这样就没有办法从A页面中传递任何的请求范围(request-scoped)数据,在这样的目的使用request范围的变量非常方便,也是很常用的。
    Tomahawk sandbox库中包含了s:redirectTracker组件,可以临时将request-scoped变量保存在http session中,这样使用规则就可以在不丢失request范围变量的情况下使用,但是这要求存在http session,并且效率低下。
    MyFaces FAQ
    1.     什么是shared项目?
    如果myfaces-core、tomahawk、tobago、trinidad都是完全独立的项目,那么就不需要shared代码项目,每个项目都在各自的名称空间内维护代码。但是,会存在大量的重复代码,并且工作量的浪费。因为它们都属于myfaces项目,所以多个子项目可以共用的代码放在shared项目中,以便减少开发和维护的工作量。
    但是,这些子项目又都有各自的独立释放周期。并且,tomahawk/tobago/trinidad都应该运行在任意的JSF实现上,而不仅仅是myfaces-core。目前使用的解决办法是重命名shared的类的报名。代码被重命名成org.apache.myfaces.shared_impl、org.apache.myfaces.shared_tomahawk等等。这样每个子项目就可以在发布的时候包含所有的支持类,而不是一个独立的共享jar文件,也不需要关心相同类的定义的冲突。对于特定项目的升级不会影响到相同环境下的其他项目。
    注意最近的MyFaces项目的释放都是用了shared库,并在源代码的jar文件中包含了shared项目的源代码,所以不需要添加额外的源代码jar文件。
    2.     如何从MyFaces获取格式良好的HTML输出?
    JTidy项目提供了一个ServletFilter,可以将响应的消息在输出前进行重新的格式化。Mozilla Firefox浏览器提供了一个扩展的View Formatted Source功能。
    在一些版本的MyFaces中,可以通过设置org.apache.myfaces.PRETTY_HTML来在web.xml文件中启用pretty输出。但是,这个选项从来都没有被很好的支持,因为需要所有的renderer来支持,以便其工作。可能在以后的MyFaces发布中移除。
    3.     MyFaces Core和tomahawk发布包中的版本号表示什么?
    MyFaces Core使用三个部分来表示版本,例如1.1.1。但是这个值和普通的版本号计数是不同的。强两个数字表示了JSF规范的版本。因为二进制的JSF规范的API没有改变,前两位数字相同的发布包被认为是兼容的,所有使用JSF指定特征的既存代码会同样可以使用。
    Tomahawk库也适用相同格式的版本号。但是因为JSF 1.2规范是向后兼容的,即兼容JSF 1.1规范,所有Tomahawk发布的版本中,1.1.x同样可以工作在JSF 1.2上。注意,tomahawk释放不保证二进制向后兼容。
    4.     为什么DataModel不是可序列化的?
    DataModel类(在UIData组件中使用)在显示和恢复视图阶段不需要保存任何任何状态。因此,不需要将它定义为可序列化的。
    如果需要定义可序列化的managed bean,并且它包含一个DataModel类型的成员变量,那么将成员变量定义为transient。
    5.     为什么时间显示不正确?
    JSF规范要求默认的date->String转换器使用标准的UTC时区,也叫做GMT时区。
    MyFaces 1.1.0或者早期的发布并没有遵循JSF规范,它们默认的使用了的时区。
    可以通过显示试用转换器来进行时区的控制,例如:
   
    或者
   
    #{bean.timeZone}返回字符串id或者TimeZone实例。
    当然,也可以注册自定义的converter来覆盖标准的converter,使自定义代码适用于所有的时间到字符串的转换。
    6.     如何在一个managed bean中访问另外一个managed bean?
    有两种方法来实现访问同一个webapp中的其他managed bean:
    使用依赖注入:在faces配置文件中定义managed beans,managed bean的属性可以被声明成到其他managed bean的引用:
   
       neededBean
       fqn.to.NeededBean
       session
    

    
       usingBean
       fqn.to.UsingBean
       request
      
         neededBean
         #{neededBean}
      

    

    限定条件如下:
    using bean必须的生命周期小于或者等于被引用的needed bean
    using bean必须具有setter方法,以needed bean作为参数
    beans不能管理彼此的依赖。
    使用查询机制:下面的代码可以在MyFaces 1.1中来显示的通过名字查询任意的managed bean。
    FacesContext facesContext = FacesContext.getCurrentInstance();
    NeededBean neededBean
        = (NeededBean) facesContext.getApplication()
            .getVariableResolver().resolveVariable(facesContext, "neededBean");
    在MyFaces 1.2中,不使用上述的方法,而是推荐使用:
    ELContext elContext = FacesContext.getCurrentInstance().getELContext();
    NeededBean neededBean
        = (NeededBean) FacesContext.getCurrentInstance().getApplication()
            .getELResolver().getValue(elContext, null, "neededBean");
    同样,可以使用这个代码来计算任意的JSF表达式:
    FacesContext facesContext = FacesContext.getCurrentInstance();
    NeededBean neededBean
        = (NeededBean)facesContext.getApplication()
          .createValueBinding("#{neededBean}").getValue(facesContext);
    7.     如何得知一个managed bean的属性是否都被设置?
    managed bean必须具有一个默认的无参构造器。所有的managed-property声明会调用合适的setter方法。但是,通常在所有的bean属性被定义后进行一些初始化的操作。
    Spring提供了post-inistialisation回调功能,任何实现InitializingBean的bean会调用afterPropertiesSet方法。在JSF中没有准确的等价操作,但是有一些接近的方式:
    定义bean的setter方法,例如public void setInitialized(boolean state)
    将下面的属性作为managed bean的最后一个属性
    
        ....
       
          initialized
          true
       

    

    JSF规范要求managed properties根据它们声明的顺序进行初始化。所以setInitialized方法会在所有其他属性被调用后进行设置。
 

[1]   

【责编:landy】

--------------------next---------------------

阅读(166) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~