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}" /> 中国网管联盟www.bitscn.com
<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。
一些其他的组件,例如文件上传需要解析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>
<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>
<!-- 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>
<!-- 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进行缓存,会增加内存的使用。
如果只是使用标准的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.apache.org/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)的释放版本兼容。
同时支持一些其他的版本,但是不做任何保证。
Getting Started
开始Apache MyFaces的第一步应该是查看一下样例应用程序。可以在http://www.irian.at/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组件在页面的后面发生)。
将输入组件设置为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阶段还没有执行。
对于页面中的非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
中国网管论坛bbs.bitsCN.com
中国网管联盟www、bitsCN、com
