
3.3 Struts 2入门案例
创建一个基于Struts 2的Web应用程序,需要完成以下5项工作。
1)创建Dynamic Web project工程,并搭建其支持Struts 2的开发环境。
2)设计模型层(设计有关的模型类),设计有关的业务逻辑处理。
3)设计控制层(设计有关的Action类),实现模型和视图之间的交互。
4)设计视图层(设计有关的JSP页面),实现信息的提交与显示。
5)修改映射文件struts.xml,在其中添加有关的Action和视图类之间的映射。
3.3.1 入门案例1——基于Struts 2计算任意两个数据的和
【例3-1】设计Web程序,其功能是用户输入两个整数,提交给Action,在Action中计算这两个数的和值,若和值为非负数,则跳转到positive.jsp页面,否则跳转到negative.jsp页面。运行界面如图3-3所示。其中图3-3a所示是输入界面,而图3-3b、图3-3c所示是显示结果界面。

图3-3 【例3-1】的运行界面
a) 输入界面b) 结果为非负数的输出界面c) 结果为负数的输出界面
【分析与设计】根据基于Struts 2开发的步骤,需要做的具体工作如下。
1)模型组件的创建:创建一个模型类Add.java,实现求和并保存和值。
2)控制器组件的创建:创建一个Action类AddAction.java,该类有一个属性——Add add),利用该属性,调用模型Add完成业务处理,并返回“页面逻辑值”,当返回值为“-”时,表示计算结果为负数,否则,当返回值为“+”时,表示计算结果为非负数。
3)视图组件的创建:视图组件有3个JSP页面:提交数据页面(input.jsp),显示结果为非负数的positive.jsp页面,以及显示结果为负数的negative.jsp页面。
4)在配置文件struts.xml中,添加Action与视图之间的映射关系,即AddAction与positive.jsp和negative.jsp之间的关系。
它们之间的逻辑关系如图3-4所示。

图3-4 【例3-1】的各组件之间的逻辑关系
【实现】按照设计步骤,依次实现。
1)创建Web工程struts2Add,并导入Struts 2必需的jar包(见3.2节)。
2)在web.xml中添加对Struts 2支持的核心控制器(具体配置信息见3.2节)。
3)编写模型类:Add.Java(设计的类必须满足JavaBean规范),其代码如下。

4)设计业务控制类(Action类):AddAction.java。
该控制器的属性为Add add,通过add接受用户提交的数据x和y,计算并获得sum值,该Action与网页positive.jsp或negative.jsp共享数据add,其代码如下。

5)修改struts.xml配置文件,添加Action的配置信息。

这些配置信息是Action与JSP页面之间关联的信息,其具体配置格式和配置内容将在后面给出详细说明。
6)编写JSP页面:需要3个页面:input.jsp、positive.jsp和negative.jsp。
① 提交数据页面:input.jsp,主要代码如下。

② 代数和为非负数时要跳转到页面positive.jsp,主要代码如下。

③ 代数和为负数时要跳转到页面negative.jsp,主要代码如下。
<body>代数和为负整数,其和值为:${add.x}+${add.y}=${add.sum}</body>
7)将该工程添加到服务器Tomcat中并运行,运行界面如图3-3所示。
提示:各组件实现数据共享是通过“Action容器”实现的,所以,在提交页面和从Action实现的跳转页面中所共享的数据,必须与Action属性一样。
3.3.2 入门案例2——基于Struts 2实现用户注册与登录
【例3-2】在Web应用程序中,一般都需要实现用户注册与用户登录子系统。在本例中基于Struts 2开发设计用户注册与登录功能。运行界面如图3-5所示。

图3-5 【例3-2】的运行界面
a) 主页面b) 注册页面c) 注册成功页面d) 登录页面e) 登录成功页面f) 登录失败页面
【分析与设计】
1)模型组件的创建。
该系统需要数据库(MySQL数据库),对数据库进行操作,利用DAO模式,所以需要设计与数据库有关的添加、查询等操作的模型类和数据库操作的通用类。
● DBConnection:实现数据库连接和关闭的工具类。
● UserDao:实现数据库操作的DAO业务类。
● EndUser:用户JavaBean类,用于描述用户信息及有关的处理方法。
2)控制器组件的创建。
创建Action类UserAction.java,包含用户登录方法和注册方法,以及相关的属性。
3)视图组件的创建,视图组件有8个JSP页面。
● 登录页面(login.jsp):当需要登录或者注册时,显示该页面。
● 登录成功页面(login_success.jsp):用户名和密码都输入正确,显示该页面。
● 登录失败页面(login_failure.jsp):当用户名或密码不正确时显示该页面。
● 注册页面(register.jsp):当没有注册时,需要利用该页面提交并注册信息。
● 注册成功页面(register_success.jsp):当将注册信息写入数据库后,显示的页面。
● 注册失败页面1(register_failure_user.jsp):注册时,若用户已存在,提示重新注册。
● 注册失败页面2(register_failure.jsp):访问数据库出错时,给出的提示页面。
● 系统主页面(index.jsp):当正确登录后,进入系统的主页面。
4)针对所要完成的模块,单独设计了一个满足该模块的struts配置文件:struts-user.xml,并且将该配置文件包含到struts.xml配置中。
这些组件之间的组织结构如图3-6所示,其中图3-6a是src目录下的Java类和配置文件,图3-6b是视图目录下的各JSP与web.xml文件。

图3-6 【例3-2】各组件的组织结构图
a) Java类和配置文件b) 视图目录下的各JSP与web.xml文件
这些组件是基于MVC的,它们之间的逻辑关联关系如图3-7所示。

图3-7 【例3-2】的各组件之间的逻辑关联关系
【Struts 2的关键思想】
在图3-7中,视图组件与Action组件之间是“数据耦合”的,如图3-8所示。在登录页面和注册页面中分别输入域属性(name属性),与Action类中的EndUser user属性同名称,这样两者之间就可以共享数据(在Action容器内共享)。图3-8只给出了登录过程中的数据共享示例,对于注册过程是类似的,请读者自己思考并给出。

图3-8 数据之间的共享关系图
【数据库的设计】
该系统设计的数据库为struts 2,其中的数据表为user,各数据字段如表3-1所示。采用MySQL数据库,其中的数据库密码为123456,用户名为root。
表3-1 数据表结构字段

【实现】按照设计步骤,依次实现。
1)在Eclipse中创建Web工程struts2user,并导入Struts 2必需的jar包。
2)修改web.xml配置文件,在web.xml中添加配置信息。
注意:这里的1)和2)两步与【例3-1】一样,不再详述。
3)编写模型类。
● 描述用户信息EndUser类的实现,其代码如下。
package com.model.user; public class EndUser{ private Integer userId; private String userName; private String userPassword; private String userRealName; //这里省略了所有属性的setter/getter方法 }
● 数据库连接及关闭工具类DBConnection的实现,其代码如下。
package com.db_util; import java.sql.*; public class DBConnection { private static String driverName="com.mysql.jdbc.Driver"; //MySQL数据库驱动程序 private static String userName="root"; //数据库用户名 private static String userPwd="123456"; //数据库用户密码 private static String dbName="struts2"; 数据库名称 public static Connection getDBConnection(){ //获取数据库连接对象的方法 String url1="jdbc:mysql://localhost/"+dbName; String url2="? user="+userName+"&password="+userPwd; String url3="&useUnicode=true&characterEncoding=utf-8"; String url=url1+url2+url3; //形成数据库连接字 Connection con=null; try { Class.forName(driverName); //加载驱动 con=DriverManager.getConnection(url); //获取数据库连接对象 } catch (Exception e) {e.printStackTrace(); } return con; } //关闭数据库连接的各资源对象 public static void closeDB(Connection con, PreparedStatement pstm, ResultSet rs){ if(rs! =null) try {rs.close(); } catch (SQLException e) {e.printStackTrace(); } if(pstm! =null) try {pstm.close(); } catch (SQLException e) {e.printStackTrace(); } if(con! =null) try {con.close(); } catch (SQLException e) {e.printStackTrace(); } } }
● 数据库操作的DAO类UserDao类的实现。
其代码如下,这里只给出了两个方法,即添加记录和查找记录的方法。
package com.dao.user; //省略了import导入包; public class UserDao { public int save(EndUser user) { //向数据库中插入一个用户的方法 Connection con=null; PreparedStatement pstmt=null; ResultSet rs=null; con=DBConnection.getDBConnection(); int row=0; String sql="insert into user(userName, userPassword, userRealName) values(? , ? , ? )"; try { pstmt=con.prepareStatement(sql); pstmt.setString(1, user.getUserName()); pstmt.setString(2, user.getUserPassword()); pstmt.setString(3, user.getUserRealName()); row=pstmt.executeUpdate(); } catch (Exception e) {e.printStackTrace(); } finally{DBConnection.closeDB(con, pstmt, rs); } return row; } public EndUser find(EndUser user) { //从数据库中查找一个用户,用于验证是否注册 Connection con=null; PreparedStatement pstmt=null; ResultSet rs=null; con=DBConnection.getDBConnection(); EndUser user2=null; String sql="select * from user where userName=? and userPassword=? "; try { pstmt=con.prepareStatement(sql); pstmt.setString(1, user.getUserName()); pstmt.setString(2, user.getUserPassword()); rs=pstmt.executeQuery(); if(rs.next()){ user2=new EndUser(); user2.setUserId(rs.getInt("id")); user2.setUserName(rs.getString("userName")); user2.setUserPassword(rs.getString("userPassword")); user2.setUserRealName(rs.getString("userRealName")); } } catch (Exception e) {e.printStackTrace(); } finally{DBConnection.closeDB(con, pstmt, rs); } return user2; } }
4)设计控制类(Action类):UserAction.java。
该类利用模型层中的两个类:EndUser和UserDao,给出对应的两个属性,通过Action方法实现所要求的功能,并利用user属性实现与视图层的数据共享。代码如下。
package com.action.user; //省略了import导入包; public class UserAction { private EndUser user; private UserDao userDao = new UserDao(); public String userLogin() throws Exception {//用户登录Action方法 String forward = null; EndUser user2 = userDao.find(user); if(user2! =null){forward="success"; }else{ forward="failure"; } return forward; } public String userRegister() throws Exception {//用户注册Action方法 String forward="error"; //数据库存数据时出错标记值 int flag = 0; EndUser user2=(userDao.find(user)); if (user2! =null){forward = "error_user"; //用户名已被占用标记标记值 } else { flag = userDao.save(user); if (flag = = 1) {forward = "success"; //成功注册标记值} } return forward; } //这里省略了属性user的set/get方法,对于属性UserDao userDao,不需要set/get方法 }
5)配置Action形成单独的配置文件struts-user.xml,并将其包含到struts.xml文件中。文件struts-user.xml的具体配置信息如下(注意该文件的格式与struts.xml一样,这里省略了文件头部分),在该配置中,配置了两个Action。

将配置文件包含到struts.xml中,其内容如下。
<struts> <include file="struts-user.xml" /> </struts>
提示:这种配置方式是便于模块化开发系统,每个模块都有单独的配置文件,易于维护和扩展。
6)编写JSP页面。
视图组件有8个JSP页面,这里只给出每个页面的body标签内的内容。
● 登录页面(login.jsp),当需要登录或者注册时,显示的初始页面。
<body> <form method="post" action="/struts2user/user/logincheck"> <table> <tr><th colspan="2">用户登录</th></tr> <tr><td align="right">用户名:</td> <td><input name="user.userName" /></td> </tr> <tr><td align="right">密码:</td> <td><input type="password" name="user.userPassword" /></td> </tr> <tr><td align="left"><input type="submit" value="登录" /></td> <td>未注册者,请先注册,单击<a href="/struts2user/user/register.jsp">注册</a></td> </tr> </table> </form> </body>
● 登录成功页面(login_success.jsp),当用户名和密码都正确时,显示该页面。
<body> 欢迎你,${user.userRealName},你登入成功!! <br> 进入主页面,请点击<a href="/struts2user/index.jsp">主页面</a> </body>
● 登录失败提示页面(login_failure.jsp),当用户名或密码不正确时,显示该页面。
<body> <h2 align="center"> <font color="red">对不起,你填写的账号和密码不正确!请</font> <a href="/struts2user/user/login.jsp">重新登录</a> </h2> </body>
● 注册页面(register.jsp),注意采用js脚本实现输入验证。
<head><title>注册页面</title> <script type="text/javascript"> function isValidate(form) { var username=document.getElementById("username").value; var userpass=document.getElementById("userpassword").value; var userpass1=document.getElementById("userpass1").value; if (userpass ! = userpass1) { alert("两次密码输入不一致,请重新输入!"); return false; }else if (userpass.length<=0 ||username.length<=0 ) { alert("用户名以及密码不能为空,请重新输入!"); return false; } else{return true; } } </script></head> <body> <h3 align="left">欢迎注册我们的系统,请认真填写您的信息</h3> <form name="register" action="/struts2user/user/register" method="post" onsubmit="return isValidate()"> <table> <tr><td align="right">账户名:</td> <td><input name="user.userName" id="username"></td></tr> <tr><td align="right">为您的账户设置密码:</td> <td><input type="password" name="user.userPassword" id="userpassword"></td></tr> <tr><td align="right">再次确认您的密码:</td> <td><input type="password" name="userpass1" id="userpass1"></td></tr> <tr><td align="right">真实姓名:</td> <td><input name="user.userRealName"id="userrealname"></td></tr> <tr><td align="right"><input type="submit" value="提交"></td> <td colspan="2"><input type="reset" value="重新填写"></td></tr> </table> </form> </body>
对于页面register_success.jsp、register_failure_user.jsp、register_failure.jsp、index.jsp与登录成功、失败页面类似,这里就不再给出介绍。
本节通过两个简单的案例,较详细地给出了基于Struts 2开发应用程序的思想、方法和步骤。这是深入学习和掌握Struts 2所必需的基础知识。在后面的章节中,将Struts 2按MVC三层结构,分别给出M、C、V有关内容的详细说明与应用设计。
提示:Struts 2的中文乱码问题处理:在视图页面与Action进行信息交互时,若有中文字符,会出现乱码问题。Struts 2中有两种方法可以解决这个问题:①设置JSP页面的pageEncoding="UTF-8"。②如果JSP页面的pageEncoding="GBK",那么需要在源包(src)下建立一个属性文件struts.properites,并在该文件内填写如下内容,修改有关的属性值:struts.locale=zh_CN, struts.i18n.encoding=gbk