Struts 2核心技术与Java EE框架整合开发实战

17.3Struts2整合JSF

目前基于JSF规范较成熟的框架有两个,一个Sun的JSFUI,另一个是Apache的MyFaces框架。因为Struts2提供了对MyFaces更好的插件支持,因此本示例采用Apache的MyFaces。整合之前让我们先来比较一下这两种表示层的框架。

17.3.1Struts2整合JSF的优点

下面从不同方面比较一下Sturts2与JSF各自的特点。

首先,在标签库方面,Struts2的标签库相对要少一些,且不可以自定义;而JSF可以自定义标签。JSF框架拥有丰富的页面组件,如果需要的话可以自己编写相应的组件,或者扩展组件;而在JSP的页面中JSF提供了页面验证标签,可以做简单的长度和类型的验证。Struts2的验证可以有两种方式,form验证与validator验证,功能上要比JSF强大。JSF的组件都是绑定到Bean的,而且数据验证的方法也可以绑定,这一点可以增强验证的功能。而对于验证的错误提示信息,它们都提供了国际化,使得验证更人性化。

其次比较一下导航,二者之间的导航功能的相同点是都通过在XML文件中配置导航规则。Struts2的XML中配置页面跳转的类型,如转发,重定向,由Action返回的字符串来决定导航的目标;而JSF在导航规则中设定页面导航,当某个页面请求到来时,根据导航规则调用指定的Action方法进行处理,并返回一个逻辑视图,然后跳转到与逻辑视图对应的页面。JSF同时支持在页面中绑定按钮触发Action的具体方法,导航原理也是一样的。

最后比较一下Struts2与JSF处理请求的方式。Struts2调用指定的方法处理请求(如果没有指定具体方法则默认调用excute方法)。JSF采用了普通的POJO类作为它的Action,将Action类绑定到页面组件,通过值变监听与事件监听进行请求处理。相比之下,JSF处理请求的方式要比Struts2复杂,不方便系统升级。

总而言之,如果将JSF做为Struts2的视图层,用Struts2的Action做模型,可以开发出完美的应用系统。

接下来就将讲解Struts2与JSF的结合使用。

17.3.2Struts2与JSF整合过程

每种框架都有它独到的设计之处,Struts2的可扩展性使得它的生命力非常顽强。Struts2提供了多种框架的插件包,它与MyFaces整合就是利用插件来实现的。下面我们介绍如何进行二者的结合应用。

首先下载Struts2的JSF插件,下载地址是http://struts.apache.org/downloads.html。目前最高的插件版本是2.0.11,我们使用这个最新的版本与myfaces进行整合。

Apache的MyFaces下载地址是http://myfaces.apache.org./download.html,目前的最高版本是1.2.2,本示例使用的是1.1.5。下载后得到名为myfaces-core-1.1.5的压缩文件,将该文件解压,得到lib包下的运行库文件(.jar文件)。

17.3.3整合应用实例

2008年是奥运年,因此我们采用目前最流行的奥运啦啦队员的选拔活动为主题,设计一个Struts2+JSF应用的示例。

奥运啦啦队员选拔队员的设计分为3种功能:增加选手、查询选手、修改选手。

按如下的顺序创建示例程序。

(1)配置环境:配置Struts2+JSF整合过程的运行环境。

(2)配置struts.xml文件:配置JSF拦截器与请求的Action。

(3)创建页面:注册选手页面,显示所有选手列表页面,修改选手页面。

(4)创建JavaBean。选手信息类PlayerInfo与控制器类OlympicAction。

(5)配置Web应用文件:配置Struts2请求转发控制器。

(6)发布运行:演示发布运行后的结果页面。

下面详细介绍各个环节的实现过程。

(1)配置应用程序运行环境。

添加Struts2核心资源包、Struts2的JSF插件包、MyFaces资源包。

(2)配置struts.xml。

利用Struts2+JSF开发视图层,需要的配置文件是struts.xml。这个文件配置信息分为两个部分,一个是JSF拦截器的配置,另一个是Struts2的Action配置。

首先看一下JSF拦截器的配置:

在struts.xml文件中需要配置JSF的拦截器,使得所有的JSF的请求都能被正确处理。这个拦截器在Struts的插件包中已经定义好了,继承这个包就可以使用这些拦截器。拦截器的配置如代码17-17所示。

代码17-17struts.xml中JSF拦截器的配置

<!--重写拦截器,将其命名在包myJSF中-->

<packagename="myJSF"extends="JSF-default">

<interceptors>

<interceptor-stackname="JSFFullStack">

<interceptor-refname="params"/>

<interceptor-refname="basicStack"/>

<interceptor-refname="JSFStack"/>

</interceptor-stack>

</interceptors>

<default-interceptor-refname="JSFFullStack"/>

</package>

接下来配置请求的Action。

在请求的Action配置中需要继承myJSF拦截器,用来处理JSF页面的组件。

本应用中来自页面的请求共有4种,如下所示。

lwelcome.action:请求显示欢迎页面。配置的result类型为JSF,使用JSF解析welcome.jsp页面中的组件。

lview.action:请求显示所有选手列表。配置的result类型为JSF,使用JSF解析view.jsp页面中的组件,显示所有已报名选手的列表信息。

lregister.action:请求增加选手。配置两种result。

result的name=“JSF”,使用JSF解析register.jsp页面中的组件。

result的name=“view”、type类型为chain,即增加选手后直接请求显示所有选手类表的view.action。

lfindone.aciton:请求显示修改选手信息

result类型为“JSF”,使用JSF解析findone.jsp页面中的组件。

result的name=“view”、type类型为“chain”,即修改选手后直接请求显示所有选手类表的view.action。

struts.xml的详细配置,如代码17-18所示。

代码17-18struts.xml的详细配置

<!--定义我们的Action的包,并且要继承myJSF-->

<packagename="olympic"extends="myJSF">

<!--首次进入的欢迎页面-->

<actionname="welcome">

<resulttype="JSF"/>

</action>

<!--显示所有选手-->

<actionname="view"class="com.sunyang.olympic.OlympicAction">

<resultname="success"type="JSF"/>

</action>

<!--选手报名-->

<actionname="register"class="com.sunyang.olympic.OlympicAction">

<resultname="success"type="JSF"/>

<resultname="view"type="redirect">view.action</result>

</action>

<!--修改信息-->

<actionname="findone"class="com.sunyang.olympic.OlympicAction"method="findone">

<resultname="success"type="JSF"/>

<resultname="view"type="redirect">view.action</result>

</action>

</package>

(3)创建页面。

示例程序共设计4个页面,分述如下。

lwelcome.jsp。欢迎页面。该页面显示两种链接:“我现在就要报名”链接到新增加选手页面;“我想看看都谁报名了”链接到查看所有选手列表信息页面。

lregister.jsp。增加用户页面。单击欢迎页面的“我现在就要报名”,跳转到本页面。该页面显示增加选手的信息,页面使用JSF输入组件标签,表单提供用户编号、姓名、年龄、性别与联络方式,所有的信息都由选手填写,而且实现数据验证功能,对于不合法的数据类型同时提供错误信息。

lview.jsp。查看所有选手列表信息。该页面采用JSF制表标签,循环遍历显示所有选手信息。该页面还提供了另一个链接“这里还没有我”,单击该链接跳转到增加选手页面。

lfindone..jsp。该页面用来提供选手修改个人信息。

单击查看所有选手列表信息页面的选手编号,跳转到本页面。本页面采用JSF输入类标签,显示该选手编号对应的详细信息,如选手编号、选手姓名、性别、年龄及联系方式,供选手修改。修改后单击“我确定修改”,跳转到显示所有选手类表页面。

①欢迎页面welcome.jsp,如代码17-19所示。

代码17-19welcome.jsp

<%@pagelanguage="java"import="java.util.*"pageEncoding="GBK"%>

<%@taglibprefix="f"uri="http://java.sun.com/JSF/core"%>

<%@taglibprefix="h"uri="http://java.sun.com/JSF/html"%>

<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN">

<html>

<head>

<title>MyJSP'welcome.jsp'startingpage</title>

</head>

<body>

<f:view>

<h3>奥运啦啦队员海选报名啦!!!</h3>

<h3>请选择:</h3>

<h:outputLinkvalue="register.action">

<h:outputTextvalue="我现在就要报名!!!"/>

</h:outputLink>

<h:outputLinkvalue="view.action">

<h:outputTextvalue="我想看看都谁报名了!!!"/>

</h:outputLink>

</f:view>

</body>

</html>

②增加选手的注册页面register.jsp。

单击“我现在就要报名”跳转到注册页面,这个页面的代码很多,标签中的“value”值就是配置在Action中注入的player的各种属性值,如代码17-20所示。

代码17-20register.jsp

<%@pagelanguage="java"contentType="text/html;charset=GBK"%>

<%@taglibprefix="f"uri="http://java.sun.com/JSF/core"%>

<%@taglibprefix="h"uri="http://java.sun.com/JSF/html"%>

<html>

<head>

<title>注册信息</title>

</head>

<body>

<f:view>

<h3>同一个世界,同一个梦想</h3>

<h3>感谢您对奥运的支持,请填写下列信息......</h3>

<h:form>

<h:panelGridcolumns="3">

<h:outputTextvalue="选手编号"/>

<h:inputTextid="id"size="30"value="#{action.player.id}"required="true"/>

<h:messagefor="id"/>

<h:outputTextvalue="选手姓名"/>

<h:inputTextid="name"size="30"value="#{action.player.name}"

required="true">

<f:validateLengthminimum="2"maximum="100"/>

</h:inputText>

<h:messagefor="name"/>

<h:outputTextvalue="选手性别"/>

<h:inputTextid="sex"size="30"value="#{action.player.sex}"required="true">

<f:validateLengthminimum="1"maximum="6"/>

</h:inputText>

<h:messagefor="sex"/>

<h:outputTextvalue="选手年龄"/>

<h:inputTextid="age"size="30"value="#{action.player.age}"required="true">

<f:validateLengthminimum="1"maximum="100"/>

</h:inputText>

<h:messagefor="age"/>

<h:outputTextvalue="联系方式"/>

<h:inputTextid="tel"size="30"value="#{action.player.tel}"required="true">

<f:validateLengthminimum="2"maximum="13"/>

</h:inputText>

<h:messagefor="tel"/>

</h:panelGrid>

<h:commandButtonvalue="报名了"action="#{action.save}"/>

<br/>

</h:form>

</f:view>

</body>

</html>

③查看所有选手信息列表页面view.jsp。

在注册选手页面,填写选手信息后,单击“报名了”跳转到查看所有选手的页面,该页面将所有选手信息遍历显示到表格中。标签<h:dataTable>用来显示表格,<f:facet>用来显示表头,<h:column>用来显示表列,详细的设计如代码17-21所示。

代码17-21view.jsp

<%@pagelanguage="java"contentType="text/html;charset=GBK"%>

<%@taglibprefix="f"uri="http://java.sun.com/JSF/core"%>

<%@taglibprefix="h"uri="http://java.sun.com/JSF/html"%>

<html>

<head>

<title>查看所有选手报名信息</title>

</head>

<body>

<f:view>

<h3>已经有这么多人报名了!!!!!</h3>

<h3>这里有我吗?没有请单击加入他们</h3>

<h:dataTablevalue="#{action.select}"var="p"style="text-align:center;width:500px"

border="1">

<h:column>

<f:facetname="header">

<h:outputTextvalue="选手编号"/>

</f:facet>

<h:outputLinkvalue="findone.action">

<f:paramname="playerid"value="#{p.id}"/>

<h:outputTextvalue="#{p.id}"/>

</h:outputLink>

</h:column>

<h:column>

<f:facetname="header">

<h:outputTextvalue="选手姓名"/>

</f:facet>

<h:outputTextvalue="#{p.name}"/>

</h:column>

<h:column>

<f:facetname="header">

<h:outputTextvalue="选手性别"/>

</f:facet>

<h:outputTextvalue="#{p.sex}"/>

</h:column>

<h:column>

<f:facetname="header">

<h:outputTextvalue="选手年龄"/>

</f:facet>

<h:outputTextvalue="#{p.age}"/>

</h:column>

<h:column>

<f:facetname="header">

<h:outputTextvalue="联系方式"/>

</f:facet>

<h:outputTextvalue="#{p.tel}"/>

</h:column>

</h:dataTable>

<p>

<h:outputLinkvalue="register.action">

<h:outputTextvalue="这里还没有我!!"/>

</h:outputLink>

</p>

</f:view>

</body>

</html>

④修改选手页面findone.jsp。

单击查看显示所有选手列表页面中的选手编号,跳转到修改选手信息页面。

在findone.jsp中需要增加一个<h:inputHidden>标签用来隐式的标识“playerid”。将选手编号为“playerid”的属性值显示到页面中。最后提交action的modify方法,详细的设计如代码17-22所示。

代码17-22findone.jsp

<%@pagelanguage="java"contentType="text/html;charset=GBK"%>

<%@taglibprefix="f"uri="http://java.sun.com/JSF/core"%>

<%@taglibprefix="h"uri="http://java.sun.com/JSF/html"%>

<html>

<head>

<title>修改信息</title>

</head>

<body>

<f:view>

<h3>同一个世界,同一个梦想</h3>

<h3>感谢您对奥运的支持,请修改下列信息>>></h3>

<h:form>

<h:inputHiddenvalue="#{action.playerid}"/>

<h:panelGridcolumns="3">

<h:outputTextvalue="选手编号"/>

<h:inputTextid="id"size="30"value="#{action.player.id}"required="true"/>

<h:messagefor="id"/>

<h:outputTextvalue="选手姓名"/>

<h:inputTextid="name"size="30"value="#{action.player.name}"required="true">

<f:validateLengthminimum="2"maximum="100"/>

</h:inputText>

<h:messagefor="name"/>

<h:outputTextvalue="选手性别"/>

<h:inputTextid="sex"size="30"value="#{action.player.sex}"required="true">

<f:validateLengthminimum="2"maximum="6"/>

</h:inputText>

<h:messagefor="sex"/>

<h:outputTextvalue="选手年龄"/>

<h:inputTextid="age"size="30"value="#{action.player.age}"

required="true">

<f:validateLengthminimum="1"maximum="100"/>

</h:inputText>

<h:messagefor="age"/>

<h:outputTextvalue="联系方式"/>

<h:inputTextid="tel"size="30"value="#{action.player.tel}"required="true">

<f:validateLengthminimum="2"maximum="13"/>

</h:inputText>

<h:messagefor="tel"/>

</h:panelGrid>

<h3>感谢您对奥运的支持,提交前请确认您的信息</h3>

<h:commandButtonvalue="我确定修改!!!"id="modifyCommand"action=

"#{action.modify}"/>

<br/>

</h:form>

</f:view>

</body>

</html>

(4)创建JavaBean,包括选手信息类PlayerInfo与控制器类OlympicAction。

①首先创建选手信息类PlayerInfo.java。

该类中定义选手的各种属性,包括id(编号)、name(名称)、age(年龄)、sex(性别)、tel(联系方式),如代码17-23所示。

代码17-23PlayerInfo.java

packagecom.sunyang.olympic;

publicclassPlayerInfo{

privateintid;

privateStringname;

privateintage;

privateStringsex;

privateinttel;

//定义默认的构造函数

publicPlayerInfo(){}

//重写构造函数

publicPlayerInfo(intid,Stringname,Stringsex,intage,inttel){

this.id=id;

this.name=name;

this.sex=sex;

this.age=age;

this.tel=tel;

}

//省略属性的getter和setter方法

}

②然后创建控制器类OlympicAction.java。

OlympicAction.java用来处理增加选手页面、修改选手页面、显示所有选手列表页面、修改选手信息页面功能的各种请求。该类需要继承ActionSupport,注入选手信息类PlayerInfo对象。添加处理页面表单的方法如下。

l预存储数据:本应用没有持久化,因此这里采用预先填写3条数据,存储在List对象中。

l增加选手:单击增加选手页面中的“报名了”,触发该方法。本应用中的所有选手信息,除了预先存储的数据以外,新增加的选手数据,均存储在会话session当中。

l查询所有选手:单击“我想看看都谁报名了”或是在修改选手页面中更新选手信息后,单击“我确定修改!”时触发此方法,此方法调用存储在List中的数据,如果会话已经被创建,就返回会话当中的列表List;否则返回预先存储的列表List。

l查找单个选手:该方法根据页面表单中选手的编号,遍历会话session中存储的选手对象,并将该对象中的数据返回给修改选手信息页面,供选手修改。

l修改选手信息:当选手在修改信息页面中更新当前信息后,点击“我确定修改!”提交到本方法。此方法将根据提交的选手编号,更新存储在会话session中的对象信息,并跳转到查看所有选手信息列表页面。

具体如代码17-24所示。

代码17-24OlympicAction.java

packagecom.sunyang.olympic;

publicclassOlympicActionextendsActionSupport{

//注入选手POJO

privatePlayerInfoplayer;

//实例化选手对象

publicOlympicAction(){

player=newPlayerInfo();}

//定义选手的id属性,用来接受页面值信息

privateintplayerid;

//省略他们的getter和setter

//定义封装选手对象的list

List<PlayerInfo>list=newArrayList<PlayerInfo>();

//硬性地存储3条数据,

publicList<PlayerInfo>setValue(){

//定义会话session

HttpSessionsession=ServletActionContext.getRequest().getSession();

if(session.getAttribute("player")==null){

PlayerInfono1=newPlayerInfo(2008,"Andy","man",25,1111111111);

PlayerInfono2=newPlayerInfo(2009,"Lily","female",25,22222222);

PlayerInfono3=newPlayerInfo(2010,"Kaka","man",25,1111111111);

list.add(no1);

list.add(no2);

list.add(no3);

session.setAttribute("player",list);

}

list=(List)session.getAttribute("player");

returnlist;

}

//遍历所有的选手并将其传值到页面

publicList<PlayerInfo>getSelect(){

ListlistValue=newArrayList();

listValue=setValue();

returnlist;

}

//增加选手对象

publicStringsave(){

HttpSessionsession=ServletActionContext.getRequest().getSession();

if(session!=null){

list=setValue();

list.add(player);

session.setAttribute("player",list);

}

return"view";

}

//遍历单个选手,并将其值传到页面

publicStringfindone(){

HttpSessionsession=ServletActionContext.getRequest().getSession();

if(session!=null){

list=(List)session.getAttribute("player");

for(inti=0;i<list.size();i++){

PlayerInfop=list.get(i);

if(p.getId()==playerid){

this.player=p;

}

}

}

return"success";

}

//用来修改选手信息

publicStringmodify(){

HttpSessionsession=ServletActionContext.getRequest().getSession();

if(session!=null){

list=(List)session.getAttribute("player");

for(inti=0;i<list.size();i++){

PlayerInfop=list.get(i);

if(p.getId()==player.getId()){

list.remove(p);

list.add(player);

}

}

}

return"view";

}

}

(5)配置Web应用文件。

Struts2中使用的JSF的UI标签、JavaBean的绑定功能,都需要使用JSF的Servlet来处理。因此在Web应用文件中需要配置的JSFSevlet处理.action的所有请求,同时要配置Struts2的请求转发。如代码17-25所示。

代码17-25web.xml

<?xmlversion="1.0"encoding="UTF-8"?>

<web-appid="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">

<filter>

<filter-name>struts</filter-name>

<filter-class>org.apache.Struts2.dispatcher.FilterDispatcher</filter-class>

</filter>

<filter-mapping>

<filter-name>struts</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

<listener>

<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>

</listener>

<!--JSF的servlet-->

<servlet>

<servlet-name>faces</servlet-name>

<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>

<load-on-startup>1</load-on-startup>

</servlet>

<!--配置所有的.action的请求都有JSF的servlet处理-->

<servlet-mapping>

<servlet-name>faces</servlet-name>

<url-pattern>*.action</url-pattern>

</servlet-mapping>

</web-app>

(6)最后发布运行。

单击“我想看看都谁报名了”后,进入到查看所有队员信息的页面。

单击欢迎页面的“我现在就要表名!!!”或者页面的“这里还没有我”后进入到增加新的啦啦队选手的页面。

当单击“报名了”后,跳转到查看所有选手信息的页面。

单击选手编号,可以修改选手的各项信息。

单击“我确定修改!!!”按钮后跳转到查看所有选手页面。

至此,Struts2和JSF的整合就完成了,发布运行后,得到预期的效果。但要注意的是,示例中我们将值存储在session会话中,并没有被真正的持久化,如果要想将数据持久化,就需要增加持久层及业务层相应代码,这些操作可以参考其它整合持久化框架的章节。

相关推荐