分类:
2008-09-09 13:31:18
Apache MyFaces项目提供了以下的功能: Server Faces的实现(MyFaces API和MyFaces Impl模块) 用于构建JSF相关的web应用程序的组件库,例如MyFaces Tomahawk、MyFaces Trinidad、MyFaces Tobago。
Server 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不在一个目录中,那么就会出现问题。
这个问题的一个解决办法就是使用
Tomahawk sandbox库中包含了s:redirectTracker组件,可以临时将request-scoped变量保存在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规范,它们默认的使用了的时区。 可以通过显示试用转换器来进行时区的控制,例如:
当然,也可以注册自定义的converter来覆盖标准的converter,使自定义代码适用于所有的时间到字符串的转换。
6. 如何在一个managed bean中访问另外一个managed bean?
有两种方法来实现访问同一个webapp中的其他managed bean:
使用依赖注入:在faces配置文件中定义managed beans,managed bean的属性可以被声明成到其他managed bean的引用:
限定条件如下:
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);
[1]