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