分享

JSF Tour 5 MyFaces Intro

 chanvy 2009-01-08
Apache MyFaces项目包含了几个关于JavaServer技术的子项目。如果需要了解更多关于JavaServer 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 中国网管联盟www、bitsCN、com

    在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项目不依赖于其他的项目。它是一个独立的程序。

网管网bitsCN_com

 

    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) { 中国网管联盟www、bitsCN、com
        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]; 中国网管联盟www_bitscn_com
    }
    该方法的参数为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经常是实际显示内容的页面的上一页。 中国网管论坛bbs.bitsCN.com
    不仅仅对于用户来说是奇怪的,同时加入收藏夹的时候也比较困难,注意,收藏夹的位置一般来说是没有关系的。因为JSF应用程序通常是可交互的,并且是状态丰富的。
    另外一个缺点是在JSF视图中使用关联链接是不安全的。(例如样式表的相对路径或者图标)。当浏览器提交内容到A,并获取数据共页面B使用时,在返回页面中的relative links都会被作为相对于它(浏览器)所知道的最后的url。所以第一次查看页面B时,所有相对链接都是相对于页面A的,如果A和页面B不在一个目录中,那么就会出现问题。
    这个问题的一个解决办法就是使用<redirect/>标记来定义导航规则。这样,会使用http redirect命令锤嫠咪榔骰袢⌒乱趁妫佣〈葱心诓康膄orward。 但是,这样做效率比较低,因为浏览器需要使用第二次请求来获取它的内容,而不是在针对初始的post请求,在响应时立即获取它的内容。另外一个重要的问题就是,获取页面B的内容是在一个单独的请求中,这样就没有办法从A页面中传递任何的请求范围(request-scoped)数据,在这样的目的使用request范围的变量非常方便,也是很常用的。
    Tomahawk sandbox库中包含了s:redirectTracker组件,可以临时将request-scoped变量保存在http session中,这样使用<redirect/>规则就可以在不丢失request范围变量的情况下使用,但是这要求存在http session,并且效率低下。

中国网管联盟www、bitsCN、com


    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输出?

中国网管联盟www_bitscn_com


    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不是可序列化的? 网管联盟www.bitsCN.com
    DataModel类(在UIData组件中使用)在显示和恢复视图阶段不需要保存任何任何状态。因此,不需要将它定义为可序列化的。
    如果需要定义可序列化的managed bean,并且它包含一个DataModel类型的成员变量,那么将成员变量定义为transient。
    5.     为什么时间显示不正确?
    JSF规范要求默认的date->String转换器使用标准的UTC时区,也叫做GMT时区。
    MyFaces 1.1.0或者早期的发布并没有遵循JSF规范,它们默认的使用了服务器的时区。
    可以通过显示试用转换器来进行时区的控制,例如:
    <f:convertDateTime timeZone="Antarctica/South_Pole" .../>
    或者
    <f:convertDateTime timeZone="#{bean.timeZone}" .../>
    #{bean.timeZone}返回字符串id或者TimeZone实例。
    当然,也可以注册自定义的converter来覆盖标准的converter,使自定义代码适用于所有的时间到字符串的转换。
    6.     如何在一个managed bean中访问另外一个managed bean?

54ne.com


    有两种方法来实现访问同一个webapp中的其他managed bean:
    使用依赖注入:在faces配置文件中定义managed beans,managed bean的属性可以被声明成到其他managed bean的引用:
    <managed-bean>
       <managed-bean-name>neededBean</managed-bean-name>
       <managed-bean-class>fqn.to.NeededBean</managed-bean-class>
       <managed-bean-scope>session</managed-bean-scope>
     </managed-bean> m

 

     <managed-bean>
       <managed-bean-name>usingBean</managed-bean-name>
       <managed-bean-class>fqn.to.UsingBean</managed-bean-class>
       <managed-bean-scope>request</managed-bean-scope>
       <managed-property>
         <property-name>neededBean</property-name>
         <value>#{neededBean}</value>
       </managed-property>
     </managed-bean>
    限定条件如下:
    using bean必须的生命周期小于或者等于被引用的needed bean
    using bean必须具有setter方法,以needed bean作为参数
    beans不能管理彼此的依赖。
    使用查询机制:下面的代码可以在MyFaces 1.1中来显示的通过名字查询任意的managed bean。 54com.cn
    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() feedom.net
          .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的最后一个属性
     <managed-bean>
        ....
        <managed-property>
          <property-name>initialized</property-name>
          <value>true</value>

中国网管联盟www、bitsCN、com

        </managed-property>
     </managed-bean>
    JSF规范要求managed properties根据它们声明的顺序进行初始化。所以setInitialized方法会在所有其他属性被调用后进行设置。
 
8.     PhaseListener为什么会被调用两次?
    JSF规范要求任何JSF实现框架在启动时自动加载/WEB-INF/faces-config.xml,所以没有必要添加如下的context参数:
    <context-param>
       <param-name>javax.faces.CONFIG_FILES</param-name>
       <param-value>/WEB-INF/faces-config.xml</param-value>
    </context-param>
    如果在web.xml文件中配置了上述信息,会迫使JSF实现加载配置文件两次,所以注册了每个phase listener两次。
    9.     dataTables的Action listener和action命令没有被调用?
    如果action源(h:commandLink,h:commandButton)没有被提供,那么Action Listeners和actions就不会被激活。当action源在dataTable上时,并且dataTable的value属性指向一个请求范围的数据源,那么action源在接下来的请求中就没有被提供,例如:
    <h:dataTable value="#{requestScopedBean.dataModel.wrappedData}" /> 
            <h:column>
                    <h:commandLink value="click here" action="#{backingBean.willNotFire}" />
            </h:column>
    </h:dataTable>
    action源没有被rendered,是因为数据源在随后的请求中不存在了(在第一次响应完成后,被垃圾回收器进行了回收)。
    为了解决这个问题,使用t:saveState标记或者将request范围的bean放在session范围内:
    <t:saveState value="#{myRequestScopedBean.dataModel.wrappedData}" />
    10. 日历、树等不能工作,并发生javascript脚本错误?
    这是需要配置MyFacesExtensionFilter。
    一些MyFaces组件不仅仅包含了HTML,可能需要额外的支持脚本、样式表、图片等等。这些资源包含在MyFaces的jar文件中,Extensions Filter添加所需的代码和URL来提供这些资源给生成的HTML。
网管联盟www.bitsCN.com

    一些其他的组件,例如文件上传需要解析Multipart请求,这也是由Extensions Filter来完成的。
    使用ExtensionFilter有如下的好处:
    将MyFaces的组件和应用程序良好的隔离
    不需要在页面或者webapp中添加MyFaces其他的组件相关的代码或者资源。
    为MyFaces开发组提供了灵活的更新组件,保持透明及向后兼容的功能。
    为页面开发人员减轻了压力。
    只加载对使用组件的资源
    处理MyFaces资源缓存
    可以通过如下方式配置extension filter:
    在web.xml文件中,将filter映射到JSF页面,例如*.jsp,以便使filter可以更新页面中的资源链接;同时映射filter到/faces/myFaces/ExtensionResources/*路径,这样可以处理页面独立的资源,例如图片,javascript脚本文件以及样式表等等。下面是一个配置的例子:
    <filter>
        <filter-name>MyFacesExtensionsFilter</filter-name>
        <filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilter</filter-class>
54ne.com

        <init-param>
            <param-name>maxFileSize</param-name>
            <param-value>20m</param-value>
            <description>Set the size limit for uploaded files.
                Format: 10 - 10 bytes
                        10k - 10 KB
                        10m - 10 MB
                        1g - 1 GB
            </description> feedom.net
        </init-param>
    </filter>
网管网bitsCN_com

 

    <!-- extension mapping for adding <script/>, <link/>, and other resource tags to JSF-pages -->
    <filter-mapping>
        <filter-name>MyFacesExtensionsFilter</filter-name>
        <!-- servlet-name must match the name of your javax.faces.webapp.FacesServlet entry -->
        <servlet-name>Faces Servlet</servlet-name>
    </filter-mapping>

网管联盟www.bitsCN.com

 

    <!-- extension mapping for serving page-independent resources (javascript, stylesheets, images, etc.) -->
    <filter-mapping>
        <filter-name>MyFacesExtensionsFilter</filter-name>
        <url-pattern>/faces/myFacesExtensionResource/*</url-pattern>
    </filter-mapping>
    同样,也可以将使用url-pattern来代替serlvet-name元素。但是仍然需要/faces/myFacesExtensionResource/*这个映射。
    <!-- extension mapping for adding <script/>, <link/>, and other resource tags to JSF-pages -->
    <filter-mapping>
        <filter-name>MyFacesExtensionsFilter</filter-name>
        <url-pattern>*.jsf</url-pattern>
    </filter-mapping>
    使用这个过滤器不会对性能造成太大的影响,即不会延迟响应时间,但是由于需要在将响应写出到客户端之前在内存中将整个response进行缓存,会增加内存的使用。

中国网管论坛bbs.bitsCN.com

    如果只是使用标准的JSF组件,而不是用MyFaces扩展的组件,以t:打头的,那么就不需要使用这个filter,否则的话,就需要进行配置。
    11. “ExtensionFilter not correctly configured.” Error?
    如果发生如下的错误:
    "java.lang.IllegalStateException: ExtensionsFilter not correctly configured. JSF mapping missing. JSF pages not covered"
    并且所有的配置已经正确的设置,那么检查以下内容:
    确保配置正确,参考:http://myfaces./tomahawk/extensionsFilter.html
    如果使用Servlet 2.4,那么不能使用jsp:forward或者request.getDispatcher().forward来跳转到某个页面,因为没有执行extensions filter,作为替代方法,要使用response.sendRedirect方法。
    12. 使用tomahawk:popup标记时,发生NullPointerException,在HtmlPopupRenderer.encodedEnd
    facet的名字需要硬编码成”popup”。
    库的依赖情况
    MyFaces核心包和组件库。
    当发布MyFaces类库的时候,也就是说不是Core框架,它会和当前的MyFaces Core,Sun Mojarra(即Sun JSF RI)的释放版本兼容。
54com.cn

    同时支持一些其他的版本,但是不做任何保证。
    Getting Started
    开始Apache MyFaces的第一步应该是查看一下样例应用程序。可以在http://www./myfaces.jsf查看这些程序,或者可以通过自定义部署来完成。可以通过以下方式来部署:
    下载Tomcat5.x/Tomcat6.x
    MyFaces例子。下载最新的webapp文件(tomahawk-X.X.X-examples.zip)
    将MyFaces样例文件解压缩到指定目录。
    将任何之前在Tomcat中发布过的MyFaces应用程序清除,同时清空Tomcat的work目录。并且保证类路径或者Tomcat的lib(common/lib or shared/lib)中不存在jsf-api.jar或者jsf-impl.jar(也就是说Sun API和实现)。将simple.war文件或者其他的例子拷贝到Tomcat安装目录的webapps目录。启动Tomcat。
    也可以使用MyFaces 运行Sun JSF RI样例程序,具体介绍参考这里。
    如何在自定义的web应用程序中使用MyFaces?建议步骤:
    在MyFaces Wiki网站查看与开发环境中servlet容器的兼容性
        然后可以使用样例程序来开始开发,例如blank.war。将blank.war解压缩后,就构成了需要工作的目录结构。 中国网管联盟www_bitscn_com
    安装和配置
    1.     在没有网络连接的情况下使用Tomahawk
    tomahawk.jar文件包含了META-INF/faces-config.xml,该文件使用了到web-facesconfig_1_1.dtd的PUBLIC引用。这使得在应用程序服务器启动的时候,访问java.sun.com来参考该DTD文件。这个问题的显示如下(从Tomcat日志中获取):
    “javax.faces.FacesException:Can’t parse configuration file:jar:file:/<web-context-path>/WEB-INF/lib/tomahawk.jar !/META-INF/faces-config.xml”
    解决这个问题的办法如下:
    在tomahawk.jar文件中添加META-INF/web-facesconfig_1_1.dtd
    修改同目录的faces-config.xml的到DTD的引用:
    <!DOCTYPE faces-config SYSTEM "web-facesconfig_1_1.dtd">
    2.     Apache Tomcat作为Servlet容器。
    Apache Tomcat 5.5.x(不包括5.5.9)
    Apache Tomcat 5.5.x可以和MyFaces共同工作,所有需要的jar文件已经在MyFaces提供的war文件中包含。 54ne.com
    如果在启动时看到空白页面,那么需要移除jsp-2.0.jar和commons.el。因为Tomcat5.5.x将这些文件放在容器外,造成了和MyFaces的war文件提供的jar文件冲突。 54ne.com

 

    专题项目
    1.     JSF状态管理
    StateManager管理状态,但是作为终端用户,必须告诉它需要保存什么。这可以通过实现Serializable接口(正确的使用transient变量)或者实现StateHolder来完成。
    数据在JSF中以两种不同的方式存储:在特定范围的bean中,或者在组件树中。特定范围的bean经常是自解释的。组件的状态保存在响应中,然后再请求到达时被恢复。有效的方式是让数据存储在组件的page范围,只要页面不改变的话。注意,组件保存值的绑定和方法的绑定,通过使用EL表达式(#{}),所以他们指向的beans不在页面范围内存储到组件树中。
    MyFaces有一个SaveState组件叫做<t:saveState>,允许将数据作为组件树的一部分存储。
    Immediate属性是如何工作的?
    immediate属性可以被用来达成如下的功能:
    允许commandLink或者commandButton来将用户导航到其他页面,而不需要处理当前页面的输入域中的数据。特别是这允许在发生验证错误的情况下进行导航。典型的例子就是cancel按钮。
    允许commandLink或者commandButton在忽略一些输入域的验证来触发后台逻辑。这是上面所说的更普遍说法。 中国网管联盟www_bitscn_com
    使一个或多个输入组件具有验证高优先级。这样的话,如果同一个页面中有些低优先级的输入包含不合法的输入时,验证也不会进行。这样可以减少错误消息的显示。
    在讨论immediate之前,首先来看一下JSF请求处理生命周期:
    Restore View – 创建或者回复前一个页面
    Apply Request Values – 将组件的提交值设置为请求值
    Process Validations – 转换和验证组件的值。如果提交的值是合法的,那么将组件的值设置为提交的值。
    Update Model Values – 将后台bean的值设置为组件的值。
    Invoke Application – 执行actionListeners和actions。
    Render Response – 返回响应。
    很多开发人员认为使用immediate标记是用来使组件跳过Process Validations阶段。使用immediate属性的目的是使组件在Apply Request Values阶段被处理。
    使用immediate属性意味着组件的值会在apply-request-values阶段被验证,也就是说,在其他的non-immediate组件的值之前(这些验证是在process-validators阶段被验证)。任何被标记为immediate的输入组件如果发生炎症错误,都会导致处理在完成apply-request-values后跳转到render阶段。也就是说,如果任何immediate组件验证失败,那么non-immediate组件的错误消息不会被显示。另外,如果immediate组件的新的值和既存的value属性的值不同,那么会激活一个ValueChangedEvent,但是这个事件会在ApplyRequestValues阶段最后执行,而不是在ProcessValidations阶段最后。特别的,这意味着任意关联到这个组件的ValueChangeListener都会在其他的immediate !UICommand组件的ActionListener之前执行(假设command组件在页面的后面发生)。

网管网bitsCN.com

    将输入组件设置为immediate并不影响模型的更新,任何的新的值仍然会在Update Model阶段被注入(也就是说,在任何immediate命令组件执行之后)。注意,也可以使用ValueChangeListener直接更新模型。
    使用immediate属性使组件ActionListener或者action方法在apply-request-values阶段的最后被执行。也就是说,在任意非immediate值的验证和后台bean被更新之前执行。
    如果是返回一个导航字符串的form的action方法,那么:
    任意非null字符串会使得生命周期直接运行到render-response阶段,意味着任意非immediate组件的验证永远不会被执行。这就是为什么immediate命令组件会以自然的方式来实现cancel操作。它甚至在页面中输入域验证失败的情况。当然,也就没有什么update model阶段,也就是说用户输入的数据被丢弃。
    null返回值会导致处理正常进行,也就是说,非immediate组件被验证,然后执行update model(如果不发生验证错误)。
    如果想让actionListener方法返回void,必须调用
    facesContext.renderResponse();
    在使用immediate输入组件时最重要的问题就是用户新输入的数值并不是总能在model中访问,因为update-model阶段还没有执行。
网管网bitsCN.com

    对于页面中的非immediate输入组件,immediate命令组件的action方法访问用户输入数据的唯一方式就是通过使用组件绑定和通过名称查询来获取指定的UIComponent对象,然后调用getSubmittedValue方法来获取用户提供的原始字符串。这个值没有被转换成它的目标类型,也不会被验证。
    对于immediate输入组件,进行了转换和验证的步骤,使用对应的UIComponnet组件,是可能获取转换后的值。如果组件在页面中位于UICommand组件的前面,并且触发了ValueChangeListener,这样就会执行ValueChangeListener。
    警告:如果action方法更新模型,但是不进行导航,那么在输入组件的值通过验证并更新模型时都会覆盖后台bean的值。
    任何immediate 组件的验证失败都不会停止immediate命令组件的执行,这和non-immediate输入组件和命令组件大不相同。
    学习指南
    当学习JSF的时候,不需要仔细查看具体的实现,下面建议了一个类/方法的列表,对学习JSF和MyFaces如何实际工作的很有帮助,同时提到了对典型方法的一个简短描述。
    javax.faces.webapp.FacesServlet
    init方法用于启动基本的faces。它演示了如何使用FactoryFinder来创建LifeCycle和FacesContext工厂 网管网bitsCN.com
    service方法演示了LifeCycle对象控制整个JSF处理
    javax.faces.component.UIViewRoot
    queueEvent方法在组件决定激活一个value-change事件(或者其他类型的事件)时被调用。事件队列的有意思的地方是:
    javax.faces.component.UIInput的验证方法
    org.apache.myfaces.renderkit.html.HtmlButtonRendererBase的decode方法
    javax.faces.component.UIComponentBase
getRenderer方法演示了如何使用当前视图中的renderkit-id、组件声明的组件家族和组件声明的renderer-type名称来决定使用哪一个renderer。
    javax.faces.webapp.UIComponentTag
    createComponentInstance演示了在JSP标记引用UIComponent组件并且组建不存在于view众时,如何实例化这个组件,通过调用Application.createComponent(String),注意该createComponentInstance方法调用自己的getComponentType()方法,这个方法典型的实现了JSF终端标记类,例如org.apache.myfaces.taglib.html.HtmlCommandButtonTag。
    javax.faces.componnet.UIInput
    processDecodes、processValidators、processUpdates方法演示了表单数据是如何变成模型的数据,通过转换和验证的处理。对于多数的组件,实际的工作过程为Apply Values、Process Validators和Update Model阶段。注意,数据从表单中开始,加载到组件的提交值字段,转换成组件的本地值字段,然后拷贝到后台bean。很多条件影响到process的出口,例如rendered state、immediate state、conversion errors和validation errors。
    结合DataTable和ActionListeners

网管网bitsCN_com


    如果在dataTable的某一行包含command link或者command button,可以从javax.faces.event.ActionListener中轻松的获取bean:
    <h:dataTable value="#{ResultsBean.hitSet.hits}" var="hit">
     <h:column>
        <h:commandLink>
          <f:actionListener type="net.java.OrderActionListener" />
          <h:outputText value="Order" />
        </h:commandLink>
        ...
     </h:column>
    </h:dataTable>
    可以通过下面的java代码来获取bean:
    public class OrderActionListener implements ActionListener {
     public void processAction(ActionEvent anEvent) throws AbortProcessingException {
        YourBeanClass tmpBean = null; 
        // 事件的getComponent方法返回command link或者command button
        UIComponent tmpComponent = anEvent.getComponent();
        // 遍历command link或者command button的父组件,出口:父组件为UIData
        while (null != tmpComponent && !(tmpComponent instanceof UIData)) {
          tmpComponent = tmpComponent.getParent();
        }
        // 如果事件组件不为空,并且父组件为UIData,那么取每行的数据,如果是Bean实例,那么强制转换
        if (tmpComponent != null && (tmpComponent instanceof UIData)) {
          Object tmpRowData = ((UIData) tmpComponent).getRowData();
          if (tmpRowData instanceof YourBeanClass) { 网管网bitsCN_com
            tmpBean = (YourBeanClass) tmpRowData;
            //TODO Implementation of your method
          }
        }
        //TODO Exception Handling if UIData not found or tmpRowBean of wrong type
     }
    }
    通过Link或者Button的参数来执行方法
    一个典型的情景:
    表格显示了对象的集合,需要通过点击edit link或者button来跳转到要编辑的记录的详细页面。
    如果熟悉Struts或者其他的MVC框架,那么可能会考虑到传输一个主键来作为请求参数,在请求页面的URL中被包含。
    <a href="/appContext/someAction.do?id=1234&userAction=prepareEdit">Edit</a>
    可以使用JSTL来生成上面的内容:
    <c:url value="someAction.do" var="url"> feedom.net
       <c:param name="id" value="1234" />
       <c:param name="userAction" value="prepareEdit" />
    </c:url>
    <a href="${url}">Edit</a>
    在JSF中,有很多方法来处理这种情况,下面列举了三种,关于第一种解决方式还存在争议,这里列举出来的目的是这可能是开发人员最先想到的。
    1>     使用<f:factionListener ...>和UIData的getRowData()
    可以参考上面的集成DataTable和ActionListeners来完成。
    2>     使用f:param来传递参数
    使用JSF完成的第一个思路可能就是模拟已经将参数传递给了链接。可以通过在commandButton或者commandLink中使用f:param标记:
    <t:dataTable var="emp" .... >

 

     <h:commandLink id="editLink" action="#{employeeAction.prepareEdit}">
     <h:outputText value="#{msg.edit}"/>
     <f:param name="id" value="#{emp.id}"/>
    </h:commandLink>

 

    然后获取处理请求参数的句柄:

 

    FacesContext context = FacesContext.getCurrentInstance();
    Map map = context.getExternalContext().getRequestParameterMap();
    String employeeID = (String) map.get("id");
    上述的方式是可行的,但是需要开发人员处理String,可能需要自己编写转换器。另外,需要添加额外的代码来获取map保存的参数。有更简洁的方法来完成:
    3>     使用<t:updateActionListener .../>


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多