前言
MVC几乎是Java Web framework的结构总纲,不过纷乱的框架各有巧妙,实现各有不同。
Struts、Webwork、Spring MVC
基于C/S模式的设计结构,不隐藏基于请求/响应通讯协议内容的思维模式,早期的Web开发者,都已经习惯这样思考,学习曲线比较平滑,比较容易入门,而且Struts从诞生至今,抢占了不少市场,有大量的用户群。现在Struts和Webwork双剑合璧衍生出的Struts2是这种结构的最强代表,在普遍的企业系统中都可以胜任,是开发不错的选择。
Tapestry、Wicket、JSF
基于组件的设计结构,亮点在于转变Web应用开发的思维模式,不能说新颖,但是很具特色,也已经被开发人员所接受,Tapestry诞生的比较早,在非官方的市场风光无限,个人也非常喜欢,但是3.0和4.x版本中都有一些令人不太愉快的地方,5.0目前还没有发布正式版本(莫非要等到零八奥运。。。)。三者之中Wicket的入门最快,学习曲线最低,而且笔者提供的组件相当丰富,是一个非常优秀的轻量级框架,一些比较小的企业系统中,强烈推荐使用。
Other
还有一些其他的框架,如Echo2,Conoon,Shale,Tuibine等等,这些产品中有的自成体系,有的是其他产品的补充,虽然各有亮点,但是很难撼动前面两者的地位了。
(Java的Web Framework真是让人火大,一陀一驼的!如MS的一家言大家不买账,这种百花齐放也让人牢骚不少~~~T.T)
郁闷的JSF
同样的设计结构,Tapesty和Wicket诞生就伴随着很多的赞美,可是JSF却冷落了很多,批评和泼冷水的人居然占大多数(难道JSF是后妈生的…)。所有的JSR定义,实现由厂商来玩,基于接口编程的思维,定义与实现分离,这是不错的拆分解藕的设计,可是由于JSF定义本身不够完善合理,厂商初期的不太买账,开源的实现又不够稳定放心等等原因,果然导致JSF一片骂声。JSF真的有这么差,偶倒是觉得,见仁见智!
视图
JSF说到底MVC结构,我们就先来解决诟病最多的视图(View)层。JSF的缺省视图技术是JSP(不可能要求SUN自抽耳光,抛弃JSP是不可能滴),也是大家骂声最多的,诸如Taglib是失败的技术,不能所见即所得可视化编辑,缺少优秀的IDE支持,代码侵入严重,程序和美工不能良好分离。
View之一JSP
JSP作为J2EE中标准的视图技术,是相当稳定和强悍的。Taglib并不是一项失败的技术,不过它的确是一项学习曲线比较高的技术,在稳定的团队中有良好的表现,但是在流水的阵营中,学习和维护的成本可能真的是不太能接受的。
关于JSP和Taglib的指南,几乎使用Java开发Web应用的人都有涉猎,而且互联网上的教程例子一陀一陀的,就不说了。
引用一句老兄的话,无论多么优秀的语言,都可以写出糟糕的代码。JSP+Taglib是一项成熟和稳定技术,代码一样可以写的很优雅,不过对程序员和美工的要求都稍微都高了一点(#>.<)。
View之二Facelets
Facelets模板框架是目前JSF优秀的视图解决技术,基于标准的XML技术,让JSF的开发效率有了很大的提升。
模板
使用Facelets的具体步骤
- 下载Facelets的最新发行包
- 引入jsf-facelets.jar到项目依赖库
- 配置web.xml文件
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param> - 配置faces-config.xml文件
<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>
index.xhtml 代码 到此为止,Facelets的准备工作已经OK,正式开工!
步骤一:创建布局页面 template.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title><ui:insert name="title">Default Title</ui:insert> </title> <style type="text/css"> body { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: small; } </style> </head> <body> <div id="head"> <ui:insert name="head">Default Head</ui:insert> </div> <hr /> <div id="header"> <ui:insert name="body">Default Body</ui:insert> </div> <hr /> <div id="header"> <ui:insert name="foot">Default Foot!</ui:insert> </div> </body> </html> |
步骤二:使用布局模板index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:t="http://myfaces.apache.org/tomahawk" xmlns:d="http://demo.phoenixup.org/jsf"> <ui:composition template="/WEB-INF/template/template.xhtml"> <ui:define name="title"> This is a demo application! </ui:define> <ui:define name="head"> <div id="head"> <h2> This is HEAD! </h2> </div> </ui:define> <ui:define name="body"> 【略】 </ui:define> <ui:define name="foot"> <div id="foot"> <h2> This is FOOT! </h2> </div> </ui:define> </ui:composition> </html> |
标签内容
- ui:insert 标记定义逻辑区域
- ui:composition调用模板
- ui:define 填充区域内容
具体的标签的教程可以参考Facelets的官方文档,使用很简单。
使用JSFC
jsfc的灵感来自Tapestry,使用转义标签,可以在不破坏页面的可视编辑的前提的下,优雅的解决了程序与视图分离,代码侵入的问题,令程序员和美工都很Happy。
显示
使用Maven2管理项目的组织结构,关于Maven2的使用,可以参考官方网站的文档,在命令行键入下面命令:
mvn jetty:run
访问地址:localhost:8080/helloworld/,可以看到显示内容,不贴图了!
组件
使用厂商组件
Facelets与使用已完场的组件是非常容易的,以tomahawk为例
- 创建 Facelets 标记文件(/WEB-INF/taglibs/ tomahawk.taglib.xml文件,太长了不贴了!)
- 在 web.xml 中声明标记库
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>
/WEB-INF/taglibs/tomahawk.taglib.xml
</param-value>
</context-param>
<!--引入多个文件以分号(;)分割 --> - 用命名空间导入标记文件。
index.xhtml 代码
- <html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:ui="http://java.sun.com/jsf/facelets"
- xmlns:h="http://java.sun.com/jsf/html"
- xmlns:t="http://myfaces.apache.org/tomahawk"
- xmlns:d="http://demo.phoenixup.org/jsf">
- 使用组件
index.xhtml 代码
- <input type="text" id="startDate" jsfc="t:inputCalendar"
- value="#{helloworld.start}" renderAsPopup="true"
- popupDateFormat="yyyy-MM-dd" renderPopupButtonAsImage="true" required="true" />
说真的JSF的组件开发,并不是一件很容易的事情,但是也绝不困难,下面就是一个最经典和老土的HelloWorld的例子,这个组件例子在JSF1.2定义的基础上开发,在JSF RI1.2.04和MyFaces1.20中都能良好运行,如果有兴趣的可以看看Sun的官方文档,还有参照一下Myfaces1.20的实现。
JSF的组件生命周期
这个图(图是别人的)是从请求至响应中组件解码与编码,调用,渲染,等等一系列的动作过程。有了对JSF生命周期的理解,组件的开发就很清晰了。
1. 创建Component
- package demo;
- import java.io.IOException;
- import javax.faces.component.UIOutput;
- import javax.faces.context.FacesContext;
- import javax.faces.context.ResponseWriter;
- public class LabelComponent extends UIOutput {
- @Override
- public String getFamily() {
- return "demo.component.Label";
- }
- @Override
- public String getRendererType() {
- return "demo.renderer.Label";
- }
- }
只覆盖了两个方法,确定了组件和渲染器的关键字。
2. 创建Renderer
- package demo;
- import java.io.IOException;
- import javax.faces.component.UIComponent;
- import javax.faces.component.UIInput;
- import javax.faces.component.UIOutput;
- import javax.faces.context.FacesContext;
- import javax.faces.context.ResponseWriter;
- import javax.faces.render.Renderer;
- public class LabelRenderer extends Renderer {
- @Override
- public void encodeBegin(FacesContext context, UIComponent component)
- throws IOException {
- FacesContext.getCurrentInstance().getExternalContext().log("Start.");
- ResponseWriter writer = context.getResponseWriter();
- UIOutput ui = (UIOutput) component;
- writer.startElement("label", component);
- Object style = ui.getAttributes().get("style");
- if (null != style)
- writer.writeAttribute("style", style.toString(), null);
- Object value = ui.getAttributes().get("value");
- if (null != value)
- writer.write(value.toString());
- writer.endElement("label");
- writer.flush();
- }
- @Override
- public void encodeEnd(FacesContext context, UIComponent component)
- throws IOException {
- FacesContext.getCurrentInstance().getExternalContext().log("The end.");
- }
- }
具体的内容实现。
3. 创建Taglib
- package demo;
- import javax.el.ValueExpression;
- import javax.faces.component.UIComponent;
- import javax.faces.webapp.UIComponentELTag;
- public class LabelTag extends UIComponentELTag {
- private ValueExpression value;
- private ValueExpression style;
- public void setValue(ValueExpression value) {
- this.value = value;
- }
- public void setStyle(ValueExpression style) {
- this.style = style;
- }
- @Override
- protected void setProperties(UIComponent component) {
- if(component ==null || !(component instanceof LabelComponent))
- throw new IllegalArgumentException("Component is not deom.component.LabelComponent");
- LabelComponent comp=(LabelComponent)component;
- super.setProperties(component);
- if(value!=null)
- comp.setValueExpression("value", value);
- if(style!=null)
- comp.setValueExpression("style", style);
- }
- @Override
- public String getComponentType() {
- return "demo.component.Label";
- }
- @Override
- public String getRendererType() {
- return "demo.renderer.Label";
- }
- }
继承UIComponentELTag,指定组件和渲染器。
- xml version="1.0" encoding="UTF-8"?>
- PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
- "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd" >
- <taglib>
- <tlib-version>1.0 tlib-version>
- <jsp-version>2.0 jsp-version>
- <short-name>demo short-name>
- <uri>http://demo.phoenixup.org/jsf uri>
- <tag>
- <name>label name>
- <tag-class>demo.LabelTag tag-class>
- <attribute>
- <name>value name>
- <required>true required>
- attribute>
- <attribute>
- <name>style name>
- attribute>
- tag>
- taglib>
4. 编辑faces-config.xml、配置Facelets
- <component>
- <component-type>demo.component.Label component-type>
- <component-class>demo.LabelComponent component-class>
- component>
- <render-kit>
- <renderer>
- <component-family>demo.component.Label component-family>
- <renderer-type>demo.renderer.Label renderer-type>
- <renderer-class>demo.LabelRenderer renderer-class>
- renderer>
- render-kit>
- <tag>
- <tag-name>label tag-name>
- <component>
- <component-type>demo.component.Label component-type>
- <renderer-type>demo.renderer.Label renderer-type>
- component>
- tag>
5. 使用
- <html xmlns="http://www.w3.org/1999/xhtml"
- xmlns:ui="http://java.sun.com/jsf/facelets"
- xmlns:h="http://java.sun.com/jsf/html"
- xmlns:t="http://myfaces.apache.org/tomahawk"
- xmlns:d="http://demo.phoenixup.org/jsf">
- <label jsfc="d:label" value="#{helloworld.title}" style="color: red; size: 12" />
组件只是简单的接受两个参数value和style,作为显示的内容,和显示格式。
复合组件
Facelets还有一种复合组件的功能,下面这个例子有个演示
1. 创建复合组件
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
- xmlns:ui="http://java.sun.com/jsf/facelets"
- xmlns:h="http://java.sun.com/jsf/html"
- xmlns:t="http://myfaces.apache.org/tomahawk">
- <ui:composition>
- <input type="text" jsfc="t:inputCalendar" id="${id}"
- value="${backingbean}" renderAsPopup="true"
- popupDateFormat="${dateformat}" renderPopupButtonAsImage="true"
- required="true" />
- ui:composition>
- html>
2. 定义复合组件
<tag> <tag-name>calendar</tag-name> <source>source/calendar.xhtml</source> </tag> |