这是关于Facelets文章系列的第二篇,Facelets是用来建立JSF应用程序时的一个可供选择的表现层技术。第一篇文章提供了Facelets的介绍。在这篇文章中,Jacob将会告诉你如何在JSF应用中开始使用Facelets。
准备开始
Facelets是作为JSF的一个ViewHandler实现的,可以被简便的配置到你的应用程序中。使用Facelets很简单,确保Facelets Jar(jsf-facelets.jar)在你项目的classpath中,并且对你的faces-config.xml文件做一个简单的修改就可以了。
<faces-config> <application> <!-- tell JSF to use Facelets --> <view-handler>com.sun.facelets.FaceletViewHandler</view-handler> </application> </faces-config>
JavaServer Faces默认使用JSP文件定义视图(*.jsp)。你需要在你的WEB-INF/web.xml中修改该类型。
<context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param>
依照上面定义的context-param,你就可以开始写Facelets并且将他们保存为以.xhtml类型的文档。你现在可以开始使用Facelets了。
内建类库
Facelets使用JavaServer Faces API 中所有UIComponents的方法和你在JSP中使用的方法一样。这就意味着你可以在线的JSF的tag类库文档。
也有一个简单的JSTL的核心库提供给你<c:forEach/> 和 <c:if/>。
最后,Facelets内建了一个UI标签库提供了模板化和重用性。关于这方面更多的资料在本文的第三篇文章中。
建立一个Facelet
Facelets仅仅需要严格的XML格式。他们不依赖于XHTML语法,可以被使用到VML,SVG,甚至SOAP中。使用JSP,你通常反复的引用标签库,而Facelets只需要在编译期间找到命名空间即可,和JSPX类似。这里是一个XHTML文档的开始部分:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <title>My First Facelet</title> </head> <body> <!-- body content goes here --> Hello #{param.name}! </body> </html>
你可以把这段代码保存为test.xhtml然后即刻请求test.jsf?name=Jacob(如果你的FacesServlet被map到*.jsf)。这是一个很好的用来确保你已经正确设置Facelets的测试。
使用EL
Facelets使用JSF中新的EL-API规范。这就意味着你可以交替使用${ } 或者 #{ },而不像是JSP。如上例所示,表达式可以内嵌正则文本。
如果你使用JSF1.2,你可以添加你自己定义的ELResolvers来整合诸如Spring,EJB,JNDI等等。--全部都可以如果你需要的话。你甚至可以拥有适合你应用程序对象的ELResolvers。
EL-API是JSP2.1规范的一部分,但是不依赖于JSP或者JSF。这就意味着它可以和Facelets一起被应用到任何部署中。
使用组件
在上面的test.xhtml实例中,你发现了额外的命名空间被申明。这些和JavaServer Faces API中内建的JSP标签库的命名空间相同。再次,Facelets期望建立在常见的领域和文档之外。
当你使用Facelets中内建的JSF组件,有一个标签库文档是一件很好的事。
既然我们已经有了test.xhtml作为开始,让我们来创建一个简单的表单。将下面的代码加入你页面的body中:
<h:form> <h:inputText value="#{person.name}"/> <h:commandButton action="#{person.action}"/> </h:form>
下面的步骤将是创建你自己的backingbean(BB)并修改faces-config.xml。
别名组件(jsfc)
前面的方法使用了特殊的标签,而使得他们在一个HTML编辑器工具(比如Dreamweaver)中不是特别好看。Facelets提供了一种不同的方法,使用标准HTML元素的jsfc属性来指定你页面中的组件(非常类似于Tapestry 和 Struts Shale 的 Clay plugin)
Facelets编译器寻找文档中所有组件的jsfc属性。jsfc属性的值是页面设计者用来在该页面中取代此元素值的别名。
<input type="text" jsfc="h:inputText" value="#{foo.bar}"/>
Facelets在编译的时候将会生成一个h:inputText组件,同时将会自动配置所有合适的属性。
别名组件允许设计工具看到正常的HTML input标签,而编程人员可以将之看成是一个在jsfc属性中定义的JSF组件。
Facelet 编译
如第一篇文中提到的,Facelets只编译规范的XML。这些文件可以只有一行或者三行,唯一的要求就是它必须是合法的XML文档。
Facelets使用SAX来将文档编译成拥有TagHandlers的无状态树包含在一个Facelet对象中。Facelets是线程安全的并且可以被简单的应用在一次多请求的大规模部署中。Facelets在编译期间提供在JSF中使用到类库及视图操作的警告消息和报告。
执行Facelets编译是简单的,如下所示:
// grab our FaceletFactory and create a Facelet FaceletFactory factory = FaceletFactory.getInstance(); Facelet f = factory.getFacelet(viewToRender.getViewId()); // populate UIViewRoot f.apply(context, viewToRender);
更多关于Facelet的体系架构将会在稍后的文章中提供。
Exceptions & Debugging
Facelets注重了错误操作的解决。举个例子,有次你不小心的打错了一个属性的表达式,Facelets将会在编译期间自动生成一条如下的错误信息:
greeting.xhtml @18,97 <input action="#{success[}"> Error Parsing: #{success[}
正如你所看到的,Facelets为你提供了错误消息来告诉你什么文件哪一行哪一列发生了错误。它甚至提供了发生错误的元素及属性。
Facelets也使用java.util.logging包来打印出排错消息。你可以阅读这里来了解如何在你的JRE中设置日值。
自定义组件
Facelets允许你使用一个XML文件或者Java代码来自定义你自己的组件库,但是在此篇文中我将只会涉及使用XML配置,因为XML配置是更好的选择。
<facelet-taglib> <namespace>http://www.mycompany.com/jsf</namespace> <tag> <tag-name>bar</tag-name> <component> <component-type>javax.faces.Data</component-type> <renderer-type>com.mycompany.Bar</renderer-type> </component> </tag> </facelet-taglib>
这就是你整合你的组件到Facelets中所要做的全部工作。这些XML文档可以使用下面两种方法参考。
(译注:关于在Facelets中如何使用Tomahawk组件,可在此查阅)
Reference them in a ";" delimitted list within your web.xml under the init-param "facelets.LIBRARIES". These files are relative to your application, just like referencing Struts configuration files.
在你的web.xml的init-param?facelets.LIBRARIES?中引用,使用?;?分隔不同XML配置文件。这些文档和你的应用程序有关,就如同引用Struts的配置文件一样。
Package them in your JAR's META-INF folder with a file extension of ".taglib.xml". Facelets will automatically pick these up for compilation just as with JSP tld files.
将它们打包到你的JAR文件中的META-INF文件夹中,使用一个以?.taglib.xml?结尾的文件。Facelets将会自动将这些读入编译,就好像JSP的tld文件。
在facelet-taglib文件中,你也可以指定转换器,验证器,和自定义的TagHandler来获取文件处理的的最终控制。
自定义验证器和转换器
验证器和转换器可以在Facelet中通过同一个facelet-taglib文档描述。
<facelet-taglib> <namespace>http://www.jsfcentral.com/public</namespace> <tag> <tag-name>validateRegExp</tag-name> <validator> <validator-id>foo.bar.RegExp</validator-id> </validator> </tag> <tag> <tag-name>convertUtilDate</tag-name> <converter> <converter-id>foo.bar.UtilDate</converter-id> </converter> </tag> </facelet-taglib>
再次,由于Facelets和JavaServer Faces的API整合的很紧密,这些就是你所要定义的全部。Facelets将会自动装配这些属性到你的Converter或者是Validator对象中。
自定义组件
好的,我还没有想到但是你想在Facelets中添加你的自定义组件。这就包含了你想要为UIComponents,Validators,Converters装配各自的属性。
Facelets提供了许多基本类来使得你的工作更简单。让我们来看看<c:if>的源码,或许能给你一些好主意:
public final class IfHandler extends TagHandler { protected final TagAttribute test; protected final TagAttribute var; public IfHandler(TagConfig config) { super(config); this.test = this.getRequiredAttribute("test"); this.var = this.getAttribute("var"); } public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException { boolean b = this.test.getBoolean(ctx); if (this.var != null) { ctx.setAttribute(var.getValue(ctx), new Boolean(b)); } if (b) { this.nextHandler.apply(ctx, parent); } } }
这就是了!你将发现当文档被编译的时候Facelets使用了?好居民?法则和构造函数注入的方法。Facelets和全部标签都是无状态的,而不像JSP(添加入了tag handler对象)。更多技术方面的细节将会在一篇不同的文档中提供,所以现在酒描述tag里面是如何进行的。
TagHandler,<c:if> 继承的,是一个很好的让你可以使用开发自定义标签的基本类示例。将TagHandler想象成JSP的TagSupport或者BodyTagSupport。
在构造函数中,该Tag的表现被传递,转达了该文档的结构包含的:位置,子标签,和属性。
TagAttributes是XML文档中属性的表现。他们有众多方法来处理EL表达式和高压方法到你需要的类型中。他们也是无状态的,他们的方法必须要传递到FaceletContext中以提供当前的状态。
最后一点比较有趣的就是子元素被部分变量nextHandler表现。你可以在<c:if>的情况下添加任意多的子元素或者根本不添加。
我们自定义的组件已经写了出来,你可以继续将它添加到facelet-taglib文档中:
<facelet-taglib> <namespace>http://www.jsfcentral.com/public</namespace> <tag> <tag-name>if</tag-name> <handler-class>com.jsfcentral.IfHandler</handler-class> </tag> </facelet-taglib>
总结
这仅仅是使用Facelets框架的一个简短介绍。接下来将会有更多关于模板化,特色和定制Facelets的详细文章。