1,JSF简介
JSF是Sun公司提出的一种新的MVC规范,作为JEE规范的组成部分,并且随着JDK一起发布,从某种程序上看,JSF已经起出了传统MVC框架,非常类似于ASP.NET服务器组件的概念.严格地说,JSF并不是一个框架而是一种规范,它是JEE5.0的重要组成部分,它通过提供一种页面组件的方式,隐藏了传统Web应用开发的HTTP细节,允许开发者以传统桌面编程的方式来开发Web应用,JSF通过将后台Bean(作用等同于控制器)的属性,方法直接绑定到页面组件的value属性或者action属性,就可以非常方便地实现系统MVC控制.
Struts2提供了良好的可扩展性,借助于Struts2的JSF插件,Struts2可以实现与JSF的整合,一旦完成了Struts2和 JSF的整合,我们就可以在Struts2的JSP页面中使用JSF页面组件,并用Action来模拟JSF的后台Bean,将JSP页面的JSF组件绑定到Action属性,从而允许开发者以JSF的方式来开发Struts2应用.
JSF是一种规范,当Sun公司提出JSF规范的同时,也提供了JSF的一个参考实现(Reference Implementation,简称RI).apache也提供了JSF的另一个实现,就是MyFaces框架,目前不管是JSF RI还是MyFaces框架,都已经逐渐成熟起来,完全可作为实现项目中的MVC框架.
当Microsoft推出ASP.NET开发平台后,ASP.NET开发平台里包含了一种服务器组件概念,通过使用服务器组件,开发者能以一种开发桌面应用的方式来开发Web应用,但Web应用程序的开发与桌面应用的开发在本质上存在着太多的差异,Web应用的开发人员必须处理HTTP的细节,而且 Web应用是由一个一个页面组成,每个页面之间的信息是不连续的,是无状态的,但传统桌面程序本质上是由一个程序不断地驱动其他程序,因些各程序之间的信息是连续的.而Microsoft的ASP.NET极好地简化了Web应用的开发,在ASP.NET的服务器组件里,开发者可以直接调用服务器组件方法来取得服务器组件的值----看起来已经超出了传统Web应用的请求---响应模式.
JSF引入了大量全新的标签库,这套标签库看起来就像是普通的HTML标签一样,但它不是静态的,而是动态的.对于网面开发人员而言,使用JSF标签库与使用变通HTML标签库并没有太大的差别,网页开发人员无需理会JSF标签后端的动态部分;网页设计人员无需了解JAVA知识,甚至无需接触 JSTL这种标签库,也可以动态展现数据.对于应用程序设计人员而言,JavaServer Faces提供一个与桌面应用开发相似的模型,我们完全可以采用基于事件的编程模型,而不是请求---响应编程模型,因此避免了出现HTTP细节的问题.
从前面的介绍中可以看出,JSF已经是一个完整的MVC框架,JSF的核心就是事件驱动,类似于早期的Visual Basic编程模式,可以通过为页面中的按钮单击事件,输入框的内容改变事件提供事件响应程序来实现Web应用流程.
JSF的组件和标签的封装程度非常高,很多典型应用已经不需要开发者去处理HTTP细节了,页面操作也会被自动映射到后台的JavaBean中,处理逻辑直接访问后台的JavaBean(Back Bean也就是控制器)交互.此外,JSF提供的组件标签(非常类似于ASP.NET的服务器组件的概念),封装程度相当高,而且有很简单的编程模式,JSF的事件模型可以细化到表单里每个表单域.JSF直接使用POJO作为控制器,并且可以使用任何方法来处理用户请求.相对于 Struts2,JSF还有一个显著的优势在于丰富的组件标签,这些组件标签提供了一种事件驱动的编程模式,可以大大简化应用的开发.
从以上两者的对比可以看出Struts2和JSF有各自的优势:
Struts2更接近传统的Web编程流程,使用更加方便.
JSF的组件库能提供细致的事件模型,而且可以简化Web应用的开发.
2,使用MyFaces
通常认为JSF RI比较权威,是官方的参考实现,但过于古板,提供的组件库不够丰富;MyFaces在完全实现了JSF RI的基本功能之外,还提供了许多额外的组件库,因此在实际开发中有很大的吸引力.因此我这里使用MyFaces.
2.1 下载和安装MyFaces
MyFaces除了实现JSF RI的基本功能外,还包含了一个tomahawk额外库,这个库内包含了更多的组件,借助于tomahawk的帮助,可以提供更多页面组件.MyFaces的下载和安装请按如下步骤进行:
1,下载MyFaces必需的核心类库,登陆http://myfaces.apache.org/download.html页面,下载MyFaces的最新版本,我这里用的是MyFaces Core 1.2.3 Distribution,如果需要使用tomahawk额外标签库,则还要下载tomahawk部分.tomahawk的下载链接也可以在这个页面找到.我下载的是myfaces-core-1.2.3-bin.zip文件
2,将下载的文件\lib下的所有JAR文件复制到WEB-INF\lib目录下,此时就可以在Web应用中使用MyFaces框架了.
3,为了可以编译MyFaces程序,建议将myfaces-api-1.2.3.jar添加到JDK的环境变量classpath里,当然也可以使用 ant工具.
4,在Web应用中安装MyFaces标签库,安装MyFaces标签库根据所用的Servlet容器不同,可能会有两种做法:
对于使用支持Servlet2.4以上规范的Web容器,我们无需修改web.xml文件,如果希望在JSP页面中使用MyFaces标签库,则可以直接在JSP页面中使用如下两行来导入MyFaces标签库:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
这里看起来很奇怪,我们为什么导入的是JSF标签库而不是MyFaces的标签库呢?熟悉自定义标签库规范就知道,此处的URI只是定义标签库时指定的 URI,与实现的内容并没有必须的联系.
MyFaces的标签库文件是放在myfaces-impl-1.2.3.jar文件夹的META-INF路径下的,对于使用Servlet2.4以上规范的Web应用,会自动读取JAR文件里的TLD文件,并识别到其中的URI信息.
如果使用更早的Servlet规范的Web应用,则应该在web.xml文件中增加如下配置片段:
<taglib>
<taglib-uri>http://java.sun.com/jsf/html</taglib-uri>
<taglib-location>/WEB-INF/lib/myfaces-impl-1.2.3.jar</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jsf/core</taglib-uri>
<taglib-location>/WEB-INF/lib/myfaces-impl-1.2.3.jar</taglib-location>
</taglib>
2.2 从输入页面开始
前面已经增加了MyFaces的标签库定义,所以现在可以在JSP页面使用MyFaces标签库了,如果要查看各标签库详细信息,请参看 MyFaces下tlddoc路径下的API文档.下面是一个例子的页面代码:
<%@ page contentType="text/html; charset=GBK" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<!--该句绑定在classes下的messages的资源文件-->
<f:loadBundle basename="messages" var="msg"/>
<html>
<head>
<title>登陆</title>
</head>
<body>
<!-- 开始使用JSF的视图输出 -->
<f:view>
<h3>
<!-- 输出国际化资源文件中的国际化信息 -->
<h:outputText value="#{msg.loginHeader}"/>
</h3>
<!-- 输出login Bean类的err属性的内容 -->
<b><h:outputText value="#{login.err}"/></b>
<h:form id="loginForm">
<h:outputText value="#{msg.namePrompt}"/>
<!-- 将下面单行输入框的值绑定到login Bean类的name属性 -->
<h:inputText value="#{login.name}" /><br>
<h:outputText value="#{msg.passPrompt}"/>
<h:inputText id="pass" value="#{login.pass}"/>
<br>
<!-- 将下面按钮的动作绑定到login Bean类的valid方法 -->
<h:commandButton action="#{login.valid}" value="#{msg.buttonTitle}" />
</h:form>
</f:view>
</body>
</html>
从上面的页面代码中可以看到,MyFaces的输入页面已经不再是传统的HTML页面,它几乎完全使用了MyFaces标签来生成页面效果,这些 MyFaces标签对应一个又一个的页面组件,而这些页面组件刚会生成与之对应的HTML标签.
使用MyFaces标签时,已经不再是传统的为表单指定action属性,用于设置表单提交的URL,页面将每个表单域绑定到后台Bean的方法, 从而将页面的视图组件与后台的Bean实现关联.
在浏览器中http://localhost:8080/jsfqs/浏览该页面,可以看到变通的HTML页面的效果,表面上看来这个页面非常简单,我们通过查看生成的HTML的源代码,发现该表单的代码非常复杂,而且该表单包含了一个隐藏域,这个隐藏域通常是如下形式的的代码:
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="rO0ABXVyABNb..." />
上面隐藏域的value值通常非常长,而且在我们看来完全是一堆乱码,但这正是MyFaces实现客户端保存状态的关键,MyFaces解决了Web应用状态不连续的弊端,它将客户端状态通过隐藏域的形式保存下来,但开发者无需处理这些细节,因而提供了更好的封装.
2.3 在Web应用中配置MyFaces
前面的JSP页面已经不再是传统的HTML页面了,该页面中的表单域是直接绑定到后台Bean的,因些直接浏览该页面是无法看到正常结果的(没有 MyFaces框架的参与,就不可能实现业务组件和后台Bean的绑定),为了让MyFaces能对每个JSP页面进行处理,下面将MyFaces的 FacesServlet部署在Web应用中,下面是在web.xml文件中配置FacesServlet的配置片段:
<!-- 配置MyFaces的FacesServlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置Faces Servlet Mapping映射 -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
从上面的配置片段中可以看到,所有以JSF作为后缀的请求都会先经过FacesServlet的处理,FacesServlet就会把JSP页面中的表单域与后台Bean的属性或方法关联起来.此外,如果MyFaces需要使用多个配置文件(用于配置导航规则和后台Bean),那么还需要在web.xml 文件中指定JSF配置文件的位置,MyFaces推荐managed-bean(后台Bean)和navigation-rules分开配置,因此,MyFaces通常会有多个配置文件,多个配置文件之间以英文逗号(,)隔开,配置文件是根据ServletContext中的 javax.faces.CONFIG_FILES参数查找,即在web.xml文件中增加如下一段:
<!-- 指定MyFaces的配置文件的路径 -->
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<!-- 多个配置文件之间以,隔开 -->
<param-value>/WEB-INF/faces-config-beans.xml,/WEB-INF/faces-config-nav.xml</param-value>
</context-param>
经过上面步骤后,还应该指定页面之间的程序状态应该保存在哪里,可以选择保存在客户端,也可以选择保存在服务器端,配置程序状态的保存位置通过如下配置片段完成:
<!-- 配置MyFaces程序状态的保存位置 -->
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
因为选择将程序状态保存在客户端,所以看到在客户端的表单元素增加了一个隐藏域,该隐藏域的值就是程序状态.
2.4 实现后台Bean
从作用上看,后台Bean非常类似Struts2的Action,但作用模式完全不同,对于Struts2的Action而言,应用通过表单提交方式提交Struts2的Action;但对于MyFaces的后台Bean而言,系统直接将MyFaces标签绑定到后台Bean的属性或方法.本应用的后台Bean的代码如下:
package lee;
public class LoginBean
{
private String name;
private String pass;
private String err;
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
public void setPass(String pass)
{
this.pass = pass;
}
public String getPass()
{
return this.pass;
}
public void setErr(String err)
{
this.err = err;
}
public String getErr()
{
return this.err;
}
public String valid()
{
if (name.equals("scott") && pass.equals("tiger"))
{
return "success";
}
setErr("您的用户名和密码不符合");
return "failure";
}
}
上面的后台Bean完全是一个POJO,处理用户请求的valid方法名也可以是任意的,只要该方法可以返回一个字符串.完成后台Bean之后,还必须使用配置文件来配置该后台Bean,配置后台Bean使用标准MyFaces配置文件完成,配置该后台Bean的代码/WEB-INF/faces- config-beans.xml如下:
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
<!--配置用户bean-->
<managed-bean>
<managed-bean-name>login</managed-bean-name>
<managed-bean-class>lee.LoginBean</managed-bean-class>
<!-- 设置后台Bean实例的有效范围 -->
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>
2.5 定义导航规则
MyFaces以导航规则来决定资源的流向,导航规则指定了从某个页面开始,以该页面的提交按钮绑定的值作为逻辑视图,导航规则定义了这些逻辑视图和实际视图资源之间的对应关系.配置文件/WEB-INF/faces-config-nav.xml内容如下:
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
<navigation-rule>
<!--如果执行inputname.jsp的结果是success,则从视图页inputname.jsp到视图页greeting.jsp -->
<!-- 导航规则的输入页面 -->
<from-view-id>/login.jsp</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/greeting.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>failure</from-outcome>
<to-view-id>/login.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
从上面的分析可以看出,MyFaces的页面标签比传统的表单提交更加灵活,在传统的Web应用里,每个表单对应一次事件,当表单提交时触发submit 事件,而单个表单域则无法单独触发事件(除非使用JavaScript技术).而MyFaces则允许每个表单域单独触发事件,MyFaces允许表单域触发两类事件:
1,Value Changed:检测表单域的值的改变,当表单域值改变时,触发事件.
2,Action:表单提交时触发事件.
MyFaces还包含了IoC(依赖注入)等高级内容,这里因为主要介绍JSF和Struts2的整合,因些这里对这些内容提要不作详细介绍.
3, 安装JSF插件
借助于JSF插件的支持,Struts2可以使用JSF的页面组件,并通过Struts2的Action模拟JSF的后台Bean,也支持JSF页面组件绑定到Action属性,从而模拟一种JSF的MVC方式.
JSF插件能把JSF的每个阶段的生命周期转换成相应的Struts2拦截器,JSF插件包含了一个jsfStack拦截器栈,这个拦截器栈用于执行JSF生命周期过程,当整个构成执行结果后,Action返回一个字符串,这个字符串常量就是逻辑视图.JSF插件将JSF生命周期分解成几个拦截器和一个名为jsf的Result,因此,为了使用JSF插件,我们必须将jsfStack的拦截器栈添加到原来的拦截器栈中,并且允许配置类型为jsf的 Result.
安装JSF插件按如下步骤进行:
1,为了在Struts2应用中使用JSF组(实际上使用MyFaces组件,因为MyFaces是JSF的一个实现),必须将MyFaces的lib路径下所有的JAR文件都复制到Web应用的WEB-INF\lib\路径下.
2,将Struts2框架的lib路径下的struts2-jsf-plugin-2.0.11.2.jar文件复制到Web应用的WEB- INF\lib\路径下.
3,还需要修改web.xml文件,在该文件中增加MyFaces的支持,正如前面在web.xml文件中配置FacesServlet处理了所有以*.jsf结尾的请求,此时则应该改为处理所有以*.action结尾的请求,因此需要在web.xml文件中增加如下片段:
<!-- JavaServer Faces Servlet Configuration, not used directly -->
<servlet>
<servlet-name>faces</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- JavaServer Faces Servlet Mapping, not called directly -->
<servlet-mapping>
<servlet-name>faces</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
4,修改struts.xml配置文件,加载JSF插件中定义的jsfStack拦截器栈,为了简化拦截器的配置,在struts.xml文件中增加了如下配置片段:
<package name="jsf" extends="jsf-default">
<!-- 重新定义一个拦截栈 -->
<interceptors>
<interceptor-stack name="jsfFullStack">
<interceptor-ref name="params" />
<interceptor-ref name="basicStack"/>
<interceptor-ref name="jsfStack"/>
</interceptor-stack>
</interceptors>
<!-- 将jsfFullStack拦截器栈定义成系统默认的拦截器 -->
<default-interceptor-ref name="jsfFullStack"/>
</package>
至此,完成了JSF插件的安装,已经可以在应用中使用MyFaces页面组件了.
4, 使用JSF插件
4.1, 实现业务逻辑组件
这里展示一个JEE应用,有单独的业务逻辑组件,业务逻辑组件通过Spring容器来创建,管理.业务逻辑组件的代如下:
package lee.service;
import java.util.*;
import lee.model.Book;
public class BookService
{
private Set<Book> bookDb;
public BookService()
{
bookDb = new HashSet<Book>();
bookDb.add(new Book(1 , "Spring2.0宝典" , "全面介绍了Spring各个知识点"));
bookDb.add(new Book(2 , "轻量级J2EE企业应用实战" , "介绍实际企业的J2EE开发过程"));
}
public Set<Book> getAllBook()
{
return bookDb;
}
public Book getBookById(int id)
{
for (Book b : bookDb)
{
if (b.getId() == id)
{
return b;
}
}
return null;
}
public void addBook(Book b)
{
bookDb.add(b);
}
}
实现了上面的业务逻辑组件后,必须将业务逻辑组件配置在Spring容器中,在Spring容器中部署业务逻辑组件的配置如下:
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Spring配置文件的Schema信息 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bs" class="lee.service.BookService"/>
</beans>
因为上面的Spring容器中没有配置Action实例,我们将会通过自动装配的方式将该业务逻辑组件注入Action实例.
为了在Struts2应用中使用Spring框架,所以我们还应该在应用中安装Spring插件,将Struts2框架的lib路径下的 struts2-spring-plugin-2.0.11.2.jar文件复制到Web应用的WEB-INF\lib\路径下,还必须将 spring.jar文件也复制到Web应用的WEB-INF\lib\路径下,此外还应该在web.xml文件中配置当应用启动时自动加载Spring 容器.下面是此应用的web.xml文件的内容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="jsf" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!-- 定义Struts 2的核心过滤器 -->
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<!-- 定义Struts 2的核心过滤器拦截所有请求 -->
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 定义一个Listener,该Listener在应用启动时创建spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 定义一个Listener,该Listener在应用启动时加载MyFaces的context -->
<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>
<!-- JavaServer Faces Servlet Configuration, not used directly -->
<servlet>
<servlet-name>faces</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- JavaServer Faces Servlet Mapping, not called directly -->
<servlet-mapping>
<servlet-name>faces</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
至此,已经完成了本应用的业务逻辑层的实现,只剩下MVC的实现了.
4.2,MVC的实现
Action中的代码如下:
package lee.action;
import com.opensymphony.xwork2.ActionSupport;
import java.util.*;
import lee.model.Book;
import lee.service.BookService;
public class BookAction extends ActionSupport
{
private Book currentBook;
private int editId;
private BookService bs;
public void setBs(BookService bs)
{
this.bs = bs;
}
public void setCurrentBook(Book currentBook)
{
this.currentBook = currentBook;
}
public Book getCurrentBook()
{
//如果editId请求参数不为空,则currentBook也不为空
if (editId != 0)
{
this.currentBook = bs.getBookById(editId);
}
else if (currentBook == null)
{
currentBook = new Book();
}
return this.currentBook;
}
public void setEditId(int editId)
{
this.editId = editId;
}
public int getEditId()
{
return this.editId;
}
public List<Book> getAllBook()
{
List<Book> result = new ArrayList<Book>();
for (Book b : bs.getAllBook())
{
result.add(b);
}
return result;
}
public String save()
{
bs.addBook(currentBook);
return "list";
}
}
定义了上面的Action之后,还必须在struts.xml文件中配置该Action,配置的片段如下:
<package name="lee" extends="jsf">
<action name="list" class="lee.action.BookAction">
<result name="success" type="jsf"/>
</action>
<action name="edit" class="lee.action.BookAction">
<result name="success" type="jsf"/>
<result name="list" type="redirect">list.action</result>
</action>
</package>
这里的list Action下的success逻辑视图对应一个类型为jsf的Result,该Result会转到list.jsp页面.
为了在list.jsp页面中列出所有的Book实例,我们使用JSF的<h:dataTable ...>页面组件,当然<h:dataTable ../>组件不可单独使用,它通常和<h:column .../>组件一起使用,下面是list.jsp页面代码:
<%@ page language="java" contentType="text/html; charset=GBK"%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<html>
<head>
<title>Struts2+MyFaces+Spring整合</title>
</head>
<body>
<f:view>
<h3>Struts2+MyFaces+Spring整合</h3>
<h3>列出所有图书</h3>
<h:dataTable value="#{action.allBook}" var="b" style="text-align:center;width:500px" border="1">
<h:column>
<f:facet name="header">
<h:outputText value="图书ID" />
</f:facet>
<h:outputLink value="edit.action">
<f:param name="editId" value="#{b.id}" />
<h:outputText value="#{b.id}" />
</h:outputLink>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="图书名" />
</f:facet>
<h:outputText value="#{b.name}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="图书简介" />
</f:facet>
<h:outputText value="#{b.desc}" />
</h:column>
</h:dataTable>
<p>
<h:outputLink value="edit.action">
<h:outputText value="新增图书"/>
</h:outputLink>
</p>
</f:view>
</body>
</html>
从上面的页面文件中可以看出,上面的页面大量使用了MyFaces的页面组件,为访问Action实例的属性,MyFaces组件允许通过action引用到该页面对应的Action.例如,要访问该页面对应Action里的allBook属性,通过如下代码片段即可:
#{action.allBook} //这相当于获得Action实例的getAllBook()方法的返回值
在浏览器中直接向list.action发送请求,将可以看到列表页面的展示.可以看出,通过使用MyFaces的dataTable页面组件,可以多么方便地实现集合数据的列表显示.使用MyFaces页面组件后,还可以简化页面显示效果的控制,并可模拟JSF的MVC模式.
5, 在JSF中使用JSP标签
为了在FreeMarker模板中使用JSP标签,必须在web.xml文件中启动JSPSupportServlet,即在web.xml方便中增加如下配置:
<servlet>
<servlet-name>JspSupportServlet</servlet-name>
<servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
然后,就可以在JSF模板中使用如下方式来导入标签库:
<#assign page=JspTaglibs["/WEB-INF/sitemesh-page.tld"]>
页面使用JSP标签的用法如下:
<@page.applyDecorator page="/link.html" name="panel" />
6, 整合JSF的思考
通过前面介绍的示例,可以看到将JSF和Struts2整合的全过程,一旦通过JSF插件整合了MyFaces框架,就可以在普通JSP页面中使用 MyFaces页面组件了,并可以以MyFaces的MVC模型实现JEE系统.整合MyFaces框架存在如下优势:
1,允许在普通的Struts2页面上使用JSF页面组件.
2,完全可以使用Struts2的配置文件,无需使用JSF配置文件.
3,保留了MyFaces框架的特征,甚至可以在Struts2页面中使用MyFaces的复杂组件.
我们通过上面的例子,并没有看到MyFaces组件有更大的特性,实际上,Struts2整合了Dojo框架之后,提供了大量的页面组件,包括 FishEye效果,富文本编辑器,各种统计图表等,Struts2去借助于MyFaces页面组件效果是否有很大的必要呢?当然,MyFaces的页面组件包含了比Dojo的组件更多的功能,就是支持后台Bean绑定,可以直接将MyFaces页面组件与Struts2的Action进行绑定,这是一种模拟桌面应用的开发方式,其实Struts2已经提供了足够的功能去支持我们的Web应用,大多数情况下都不需要再与JSF整合,但是能过这篇文章,我们可以看到Struts2提供的优秀的整合其它框架的能力.
JSF是Sun公司提出的一种新的MVC规范,作为JEE规范的组成部分,并且随着JDK一起发布,从某种程序上看,JSF已经起出了传统MVC框架,非常类似于ASP.NET服务器组件的概念.严格地说,JSF并不是一个框架而是一种规范,它是JEE5.0的重要组成部分,它通过提供一种页面组件的方式,隐藏了传统Web应用开发的HTTP细节,允许开发者以传统桌面编程的方式来开发Web应用,JSF通过将后台Bean(作用等同于控制器)的属性,方法直接绑定到页面组件的value属性或者action属性,就可以非常方便地实现系统MVC控制.
Struts2提供了良好的可扩展性,借助于Struts2的JSF插件,Struts2可以实现与JSF的整合,一旦完成了Struts2和 JSF的整合,我们就可以在Struts2的JSP页面中使用JSF页面组件,并用Action来模拟JSF的后台Bean,将JSP页面的JSF组件绑定到Action属性,从而允许开发者以JSF的方式来开发Struts2应用.
JSF是一种规范,当Sun公司提出JSF规范的同时,也提供了JSF的一个参考实现(Reference Implementation,简称RI).apache也提供了JSF的另一个实现,就是MyFaces框架,目前不管是JSF RI还是MyFaces框架,都已经逐渐成熟起来,完全可作为实现项目中的MVC框架.
当Microsoft推出ASP.NET开发平台后,ASP.NET开发平台里包含了一种服务器组件概念,通过使用服务器组件,开发者能以一种开发桌面应用的方式来开发Web应用,但Web应用程序的开发与桌面应用的开发在本质上存在着太多的差异,Web应用的开发人员必须处理HTTP的细节,而且 Web应用是由一个一个页面组成,每个页面之间的信息是不连续的,是无状态的,但传统桌面程序本质上是由一个程序不断地驱动其他程序,因些各程序之间的信息是连续的.而Microsoft的ASP.NET极好地简化了Web应用的开发,在ASP.NET的服务器组件里,开发者可以直接调用服务器组件方法来取得服务器组件的值----看起来已经超出了传统Web应用的请求---响应模式.
JSF引入了大量全新的标签库,这套标签库看起来就像是普通的HTML标签一样,但它不是静态的,而是动态的.对于网面开发人员而言,使用JSF标签库与使用变通HTML标签库并没有太大的差别,网页开发人员无需理会JSF标签后端的动态部分;网页设计人员无需了解JAVA知识,甚至无需接触 JSTL这种标签库,也可以动态展现数据.对于应用程序设计人员而言,JavaServer Faces提供一个与桌面应用开发相似的模型,我们完全可以采用基于事件的编程模型,而不是请求---响应编程模型,因此避免了出现HTTP细节的问题.
从前面的介绍中可以看出,JSF已经是一个完整的MVC框架,JSF的核心就是事件驱动,类似于早期的Visual Basic编程模式,可以通过为页面中的按钮单击事件,输入框的内容改变事件提供事件响应程序来实现Web应用流程.
JSF的组件和标签的封装程度非常高,很多典型应用已经不需要开发者去处理HTTP细节了,页面操作也会被自动映射到后台的JavaBean中,处理逻辑直接访问后台的JavaBean(Back Bean也就是控制器)交互.此外,JSF提供的组件标签(非常类似于ASP.NET的服务器组件的概念),封装程度相当高,而且有很简单的编程模式,JSF的事件模型可以细化到表单里每个表单域.JSF直接使用POJO作为控制器,并且可以使用任何方法来处理用户请求.相对于 Struts2,JSF还有一个显著的优势在于丰富的组件标签,这些组件标签提供了一种事件驱动的编程模式,可以大大简化应用的开发.
从以上两者的对比可以看出Struts2和JSF有各自的优势:
Struts2更接近传统的Web编程流程,使用更加方便.
JSF的组件库能提供细致的事件模型,而且可以简化Web应用的开发.
2,使用MyFaces
通常认为JSF RI比较权威,是官方的参考实现,但过于古板,提供的组件库不够丰富;MyFaces在完全实现了JSF RI的基本功能之外,还提供了许多额外的组件库,因此在实际开发中有很大的吸引力.因此我这里使用MyFaces.
2.1 下载和安装MyFaces
MyFaces除了实现JSF RI的基本功能外,还包含了一个tomahawk额外库,这个库内包含了更多的组件,借助于tomahawk的帮助,可以提供更多页面组件.MyFaces的下载和安装请按如下步骤进行:
1,下载MyFaces必需的核心类库,登陆http://myfaces.apache.org/download.html页面,下载MyFaces的最新版本,我这里用的是MyFaces Core 1.2.3 Distribution,如果需要使用tomahawk额外标签库,则还要下载tomahawk部分.tomahawk的下载链接也可以在这个页面找到.我下载的是myfaces-core-1.2.3-bin.zip文件
2,将下载的文件\lib下的所有JAR文件复制到WEB-INF\lib目录下,此时就可以在Web应用中使用MyFaces框架了.
3,为了可以编译MyFaces程序,建议将myfaces-api-1.2.3.jar添加到JDK的环境变量classpath里,当然也可以使用 ant工具.
4,在Web应用中安装MyFaces标签库,安装MyFaces标签库根据所用的Servlet容器不同,可能会有两种做法:
对于使用支持Servlet2.4以上规范的Web容器,我们无需修改web.xml文件,如果希望在JSP页面中使用MyFaces标签库,则可以直接在JSP页面中使用如下两行来导入MyFaces标签库:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
这里看起来很奇怪,我们为什么导入的是JSF标签库而不是MyFaces的标签库呢?熟悉自定义标签库规范就知道,此处的URI只是定义标签库时指定的 URI,与实现的内容并没有必须的联系.
MyFaces的标签库文件是放在myfaces-impl-1.2.3.jar文件夹的META-INF路径下的,对于使用Servlet2.4以上规范的Web应用,会自动读取JAR文件里的TLD文件,并识别到其中的URI信息.
如果使用更早的Servlet规范的Web应用,则应该在web.xml文件中增加如下配置片段:
<taglib>
<taglib-uri>http://java.sun.com/jsf/html</taglib-uri>
<taglib-location>/WEB-INF/lib/myfaces-impl-1.2.3.jar</taglib-location>
</taglib>
<taglib>
<taglib-uri>http://java.sun.com/jsf/core</taglib-uri>
<taglib-location>/WEB-INF/lib/myfaces-impl-1.2.3.jar</taglib-location>
</taglib>
2.2 从输入页面开始
前面已经增加了MyFaces的标签库定义,所以现在可以在JSP页面使用MyFaces标签库了,如果要查看各标签库详细信息,请参看 MyFaces下tlddoc路径下的API文档.下面是一个例子的页面代码:
<%@ page contentType="text/html; charset=GBK" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<!--该句绑定在classes下的messages的资源文件-->
<f:loadBundle basename="messages" var="msg"/>
<html>
<head>
<title>登陆</title>
</head>
<body>
<!-- 开始使用JSF的视图输出 -->
<f:view>
<h3>
<!-- 输出国际化资源文件中的国际化信息 -->
<h:outputText value="#{msg.loginHeader}"/>
</h3>
<!-- 输出login Bean类的err属性的内容 -->
<b><h:outputText value="#{login.err}"/></b>
<h:form id="loginForm">
<h:outputText value="#{msg.namePrompt}"/>
<!-- 将下面单行输入框的值绑定到login Bean类的name属性 -->
<h:inputText value="#{login.name}" /><br>
<h:outputText value="#{msg.passPrompt}"/>
<h:inputText id="pass" value="#{login.pass}"/>
<br>
<!-- 将下面按钮的动作绑定到login Bean类的valid方法 -->
<h:commandButton action="#{login.valid}" value="#{msg.buttonTitle}" />
</h:form>
</f:view>
</body>
</html>
从上面的页面代码中可以看到,MyFaces的输入页面已经不再是传统的HTML页面,它几乎完全使用了MyFaces标签来生成页面效果,这些 MyFaces标签对应一个又一个的页面组件,而这些页面组件刚会生成与之对应的HTML标签.
使用MyFaces标签时,已经不再是传统的为表单指定action属性,用于设置表单提交的URL,页面将每个表单域绑定到后台Bean的方法, 从而将页面的视图组件与后台的Bean实现关联.
在浏览器中http://localhost:8080/jsfqs/浏览该页面,可以看到变通的HTML页面的效果,表面上看来这个页面非常简单,我们通过查看生成的HTML的源代码,发现该表单的代码非常复杂,而且该表单包含了一个隐藏域,这个隐藏域通常是如下形式的的代码:
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="rO0ABXVyABNb..." />
上面隐藏域的value值通常非常长,而且在我们看来完全是一堆乱码,但这正是MyFaces实现客户端保存状态的关键,MyFaces解决了Web应用状态不连续的弊端,它将客户端状态通过隐藏域的形式保存下来,但开发者无需处理这些细节,因而提供了更好的封装.
2.3 在Web应用中配置MyFaces
前面的JSP页面已经不再是传统的HTML页面了,该页面中的表单域是直接绑定到后台Bean的,因些直接浏览该页面是无法看到正常结果的(没有 MyFaces框架的参与,就不可能实现业务组件和后台Bean的绑定),为了让MyFaces能对每个JSP页面进行处理,下面将MyFaces的 FacesServlet部署在Web应用中,下面是在web.xml文件中配置FacesServlet的配置片段:
<!-- 配置MyFaces的FacesServlet -->
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置Faces Servlet Mapping映射 -->
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
从上面的配置片段中可以看到,所有以JSF作为后缀的请求都会先经过FacesServlet的处理,FacesServlet就会把JSP页面中的表单域与后台Bean的属性或方法关联起来.此外,如果MyFaces需要使用多个配置文件(用于配置导航规则和后台Bean),那么还需要在web.xml 文件中指定JSF配置文件的位置,MyFaces推荐managed-bean(后台Bean)和navigation-rules分开配置,因此,MyFaces通常会有多个配置文件,多个配置文件之间以英文逗号(,)隔开,配置文件是根据ServletContext中的 javax.faces.CONFIG_FILES参数查找,即在web.xml文件中增加如下一段:
<!-- 指定MyFaces的配置文件的路径 -->
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<!-- 多个配置文件之间以,隔开 -->
<param-value>/WEB-INF/faces-config-beans.xml,/WEB-INF/faces-config-nav.xml</param-value>
</context-param>
经过上面步骤后,还应该指定页面之间的程序状态应该保存在哪里,可以选择保存在客户端,也可以选择保存在服务器端,配置程序状态的保存位置通过如下配置片段完成:
<!-- 配置MyFaces程序状态的保存位置 -->
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
因为选择将程序状态保存在客户端,所以看到在客户端的表单元素增加了一个隐藏域,该隐藏域的值就是程序状态.
2.4 实现后台Bean
从作用上看,后台Bean非常类似Struts2的Action,但作用模式完全不同,对于Struts2的Action而言,应用通过表单提交方式提交Struts2的Action;但对于MyFaces的后台Bean而言,系统直接将MyFaces标签绑定到后台Bean的属性或方法.本应用的后台Bean的代码如下:
package lee;
public class LoginBean
{
private String name;
private String pass;
private String err;
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
public void setPass(String pass)
{
this.pass = pass;
}
public String getPass()
{
return this.pass;
}
public void setErr(String err)
{
this.err = err;
}
public String getErr()
{
return this.err;
}
public String valid()
{
if (name.equals("scott") && pass.equals("tiger"))
{
return "success";
}
setErr("您的用户名和密码不符合");
return "failure";
}
}
上面的后台Bean完全是一个POJO,处理用户请求的valid方法名也可以是任意的,只要该方法可以返回一个字符串.完成后台Bean之后,还必须使用配置文件来配置该后台Bean,配置后台Bean使用标准MyFaces配置文件完成,配置该后台Bean的代码/WEB-INF/faces- config-beans.xml如下:
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
<!--配置用户bean-->
<managed-bean>
<managed-bean-name>login</managed-bean-name>
<managed-bean-class>lee.LoginBean</managed-bean-class>
<!-- 设置后台Bean实例的有效范围 -->
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
</faces-config>
2.5 定义导航规则
MyFaces以导航规则来决定资源的流向,导航规则指定了从某个页面开始,以该页面的提交按钮绑定的值作为逻辑视图,导航规则定义了这些逻辑视图和实际视图资源之间的对应关系.配置文件/WEB-INF/faces-config-nav.xml内容如下:
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE faces-config PUBLIC
"-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN"
"http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
<navigation-rule>
<!--如果执行inputname.jsp的结果是success,则从视图页inputname.jsp到视图页greeting.jsp -->
<!-- 导航规则的输入页面 -->
<from-view-id>/login.jsp</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/greeting.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>failure</from-outcome>
<to-view-id>/login.jsp</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
从上面的分析可以看出,MyFaces的页面标签比传统的表单提交更加灵活,在传统的Web应用里,每个表单对应一次事件,当表单提交时触发submit 事件,而单个表单域则无法单独触发事件(除非使用JavaScript技术).而MyFaces则允许每个表单域单独触发事件,MyFaces允许表单域触发两类事件:
1,Value Changed:检测表单域的值的改变,当表单域值改变时,触发事件.
2,Action:表单提交时触发事件.
MyFaces还包含了IoC(依赖注入)等高级内容,这里因为主要介绍JSF和Struts2的整合,因些这里对这些内容提要不作详细介绍.
3, 安装JSF插件
借助于JSF插件的支持,Struts2可以使用JSF的页面组件,并通过Struts2的Action模拟JSF的后台Bean,也支持JSF页面组件绑定到Action属性,从而模拟一种JSF的MVC方式.
JSF插件能把JSF的每个阶段的生命周期转换成相应的Struts2拦截器,JSF插件包含了一个jsfStack拦截器栈,这个拦截器栈用于执行JSF生命周期过程,当整个构成执行结果后,Action返回一个字符串,这个字符串常量就是逻辑视图.JSF插件将JSF生命周期分解成几个拦截器和一个名为jsf的Result,因此,为了使用JSF插件,我们必须将jsfStack的拦截器栈添加到原来的拦截器栈中,并且允许配置类型为jsf的 Result.
安装JSF插件按如下步骤进行:
1,为了在Struts2应用中使用JSF组(实际上使用MyFaces组件,因为MyFaces是JSF的一个实现),必须将MyFaces的lib路径下所有的JAR文件都复制到Web应用的WEB-INF\lib\路径下.
2,将Struts2框架的lib路径下的struts2-jsf-plugin-2.0.11.2.jar文件复制到Web应用的WEB- INF\lib\路径下.
3,还需要修改web.xml文件,在该文件中增加MyFaces的支持,正如前面在web.xml文件中配置FacesServlet处理了所有以*.jsf结尾的请求,此时则应该改为处理所有以*.action结尾的请求,因此需要在web.xml文件中增加如下片段:
<!-- JavaServer Faces Servlet Configuration, not used directly -->
<servlet>
<servlet-name>faces</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- JavaServer Faces Servlet Mapping, not called directly -->
<servlet-mapping>
<servlet-name>faces</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
4,修改struts.xml配置文件,加载JSF插件中定义的jsfStack拦截器栈,为了简化拦截器的配置,在struts.xml文件中增加了如下配置片段:
<package name="jsf" extends="jsf-default">
<!-- 重新定义一个拦截栈 -->
<interceptors>
<interceptor-stack name="jsfFullStack">
<interceptor-ref name="params" />
<interceptor-ref name="basicStack"/>
<interceptor-ref name="jsfStack"/>
</interceptor-stack>
</interceptors>
<!-- 将jsfFullStack拦截器栈定义成系统默认的拦截器 -->
<default-interceptor-ref name="jsfFullStack"/>
</package>
至此,完成了JSF插件的安装,已经可以在应用中使用MyFaces页面组件了.
4, 使用JSF插件
4.1, 实现业务逻辑组件
这里展示一个JEE应用,有单独的业务逻辑组件,业务逻辑组件通过Spring容器来创建,管理.业务逻辑组件的代如下:
package lee.service;
import java.util.*;
import lee.model.Book;
public class BookService
{
private Set<Book> bookDb;
public BookService()
{
bookDb = new HashSet<Book>();
bookDb.add(new Book(1 , "Spring2.0宝典" , "全面介绍了Spring各个知识点"));
bookDb.add(new Book(2 , "轻量级J2EE企业应用实战" , "介绍实际企业的J2EE开发过程"));
}
public Set<Book> getAllBook()
{
return bookDb;
}
public Book getBookById(int id)
{
for (Book b : bookDb)
{
if (b.getId() == id)
{
return b;
}
}
return null;
}
public void addBook(Book b)
{
bookDb.add(b);
}
}
实现了上面的业务逻辑组件后,必须将业务逻辑组件配置在Spring容器中,在Spring容器中部署业务逻辑组件的配置如下:
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Spring配置文件的Schema信息 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bs" class="lee.service.BookService"/>
</beans>
因为上面的Spring容器中没有配置Action实例,我们将会通过自动装配的方式将该业务逻辑组件注入Action实例.
为了在Struts2应用中使用Spring框架,所以我们还应该在应用中安装Spring插件,将Struts2框架的lib路径下的 struts2-spring-plugin-2.0.11.2.jar文件复制到Web应用的WEB-INF\lib\路径下,还必须将 spring.jar文件也复制到Web应用的WEB-INF\lib\路径下,此外还应该在web.xml文件中配置当应用启动时自动加载Spring 容器.下面是此应用的web.xml文件的内容:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="jsf" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!-- 定义Struts 2的核心过滤器 -->
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<!-- 定义Struts 2的核心过滤器拦截所有请求 -->
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 定义一个Listener,该Listener在应用启动时创建spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 定义一个Listener,该Listener在应用启动时加载MyFaces的context -->
<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>
<!-- JavaServer Faces Servlet Configuration, not used directly -->
<servlet>
<servlet-name>faces</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- JavaServer Faces Servlet Mapping, not called directly -->
<servlet-mapping>
<servlet-name>faces</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
至此,已经完成了本应用的业务逻辑层的实现,只剩下MVC的实现了.
4.2,MVC的实现
Action中的代码如下:
package lee.action;
import com.opensymphony.xwork2.ActionSupport;
import java.util.*;
import lee.model.Book;
import lee.service.BookService;
public class BookAction extends ActionSupport
{
private Book currentBook;
private int editId;
private BookService bs;
public void setBs(BookService bs)
{
this.bs = bs;
}
public void setCurrentBook(Book currentBook)
{
this.currentBook = currentBook;
}
public Book getCurrentBook()
{
//如果editId请求参数不为空,则currentBook也不为空
if (editId != 0)
{
this.currentBook = bs.getBookById(editId);
}
else if (currentBook == null)
{
currentBook = new Book();
}
return this.currentBook;
}
public void setEditId(int editId)
{
this.editId = editId;
}
public int getEditId()
{
return this.editId;
}
public List<Book> getAllBook()
{
List<Book> result = new ArrayList<Book>();
for (Book b : bs.getAllBook())
{
result.add(b);
}
return result;
}
public String save()
{
bs.addBook(currentBook);
return "list";
}
}
定义了上面的Action之后,还必须在struts.xml文件中配置该Action,配置的片段如下:
<package name="lee" extends="jsf">
<action name="list" class="lee.action.BookAction">
<result name="success" type="jsf"/>
</action>
<action name="edit" class="lee.action.BookAction">
<result name="success" type="jsf"/>
<result name="list" type="redirect">list.action</result>
</action>
</package>
这里的list Action下的success逻辑视图对应一个类型为jsf的Result,该Result会转到list.jsp页面.
为了在list.jsp页面中列出所有的Book实例,我们使用JSF的<h:dataTable ...>页面组件,当然<h:dataTable ../>组件不可单独使用,它通常和<h:column .../>组件一起使用,下面是list.jsp页面代码:
<%@ page language="java" contentType="text/html; charset=GBK"%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<html>
<head>
<title>Struts2+MyFaces+Spring整合</title>
</head>
<body>
<f:view>
<h3>Struts2+MyFaces+Spring整合</h3>
<h3>列出所有图书</h3>
<h:dataTable value="#{action.allBook}" var="b" style="text-align:center;width:500px" border="1">
<h:column>
<f:facet name="header">
<h:outputText value="图书ID" />
</f:facet>
<h:outputLink value="edit.action">
<f:param name="editId" value="#{b.id}" />
<h:outputText value="#{b.id}" />
</h:outputLink>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="图书名" />
</f:facet>
<h:outputText value="#{b.name}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="图书简介" />
</f:facet>
<h:outputText value="#{b.desc}" />
</h:column>
</h:dataTable>
<p>
<h:outputLink value="edit.action">
<h:outputText value="新增图书"/>
</h:outputLink>
</p>
</f:view>
</body>
</html>
从上面的页面文件中可以看出,上面的页面大量使用了MyFaces的页面组件,为访问Action实例的属性,MyFaces组件允许通过action引用到该页面对应的Action.例如,要访问该页面对应Action里的allBook属性,通过如下代码片段即可:
#{action.allBook} //这相当于获得Action实例的getAllBook()方法的返回值
在浏览器中直接向list.action发送请求,将可以看到列表页面的展示.可以看出,通过使用MyFaces的dataTable页面组件,可以多么方便地实现集合数据的列表显示.使用MyFaces页面组件后,还可以简化页面显示效果的控制,并可模拟JSF的MVC模式.
5, 在JSF中使用JSP标签
为了在FreeMarker模板中使用JSP标签,必须在web.xml文件中启动JSPSupportServlet,即在web.xml方便中增加如下配置:
<servlet>
<servlet-name>JspSupportServlet</servlet-name>
<servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
然后,就可以在JSF模板中使用如下方式来导入标签库:
<#assign page=JspTaglibs["/WEB-INF/sitemesh-page.tld"]>
页面使用JSP标签的用法如下:
<@page.applyDecorator page="/link.html" name="panel" />
6, 整合JSF的思考
通过前面介绍的示例,可以看到将JSF和Struts2整合的全过程,一旦通过JSF插件整合了MyFaces框架,就可以在普通JSP页面中使用 MyFaces页面组件了,并可以以MyFaces的MVC模型实现JEE系统.整合MyFaces框架存在如下优势:
1,允许在普通的Struts2页面上使用JSF页面组件.
2,完全可以使用Struts2的配置文件,无需使用JSF配置文件.
3,保留了MyFaces框架的特征,甚至可以在Struts2页面中使用MyFaces的复杂组件.
我们通过上面的例子,并没有看到MyFaces组件有更大的特性,实际上,Struts2整合了Dojo框架之后,提供了大量的页面组件,包括 FishEye效果,富文本编辑器,各种统计图表等,Struts2去借助于MyFaces页面组件效果是否有很大的必要呢?当然,MyFaces的页面组件包含了比Dojo的组件更多的功能,就是支持后台Bean绑定,可以直接将MyFaces页面组件与Struts2的Action进行绑定,这是一种模拟桌面应用的开发方式,其实Struts2已经提供了足够的功能去支持我们的Web应用,大多数情况下都不需要再与JSF整合,但是能过这篇文章,我们可以看到Struts2提供的优秀的整合其它框架的能力.