Top

JAVA Struts2 DAY01

  1. Struts2之HelloWorld,Step1-4
  2. Struts2之HelloWorld,Step5-7
  3. 将页面表单中的数据提交给Action
  4. 使用EL表达式,显示Action中的数据
  5. NetCTOSS资费列表,Step1-3
  6. NetCTOSS资费列表,Step4-6

1 Struts2之HelloWorld,Step1-4

1.1 问题

使用Struts2框架,写一个HelloWorld示例。其中,在Action的业务方法中输出“Hello,Action.”,在JSP页面上显示“Hello,Struts2.”。

本案例要求完成Struts2使用步骤1-4,来构建起Struts2使用的准备环境。

1.2 方案

Struts2使用步骤:

  1. 创建WEB项目
  2. 导入Struts2核心包
  3. 配置前端控制器
  4. 创建struts.xml

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建WEB项目

在MyEclipse中,点击File ( New ( WebProject,如下图:

图-1

在弹出框中输入项目名,如下图:

图-2

步骤二:导入Struts2核心包

要想使用Struts2,至少需要导入5个核心包,如下图:

图-3

将这5个包复制到新创建项目的/WEB-INF/lib目录下,如下图:

图-4

步骤三:配置前端控制器

在web.xml中,配置前端控制器filter,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>	
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
#cold_bold<!--前端控制器 -->
#cold_bold<filter>
#cold_bold<filter-name>struts2</filter-name>
#cold_bold<filter-class>
#cold_bold	org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
#cold_bold</filter-class>
#cold_bold</filter>
#cold_bold<filter-mapping>
#cold_bold<filter-name>struts2</filter-name>
#cold_bold<url-pattern>/*</url-pattern>
#cold_bold</filter-mapping>
</web-app>

步骤四:创建struts.xml

在src下,创建名为struts.xml的配置文件,如下图:

图-5

注意:该配置文件必须放于src下,且名称必须为struts.xml。

打开struts.xml,设置其版本信息及DTD引用。这些内容可以从Struts2默认配置文件中直接复制过来,该默认配置文件名为struts-default.xml,位于核心包struts2-core-2.1.8.jar根路径下,如下图:

图-6

打开struts-default.xml,从中复制出该XML的版本信息及DTD,如下图:

图-7

将这些复制好的内容,粘贴到刚才创建的struts.xml中。其中注释部分可以删除,完成后struts.xml的代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
    "/struts.apache.org/dtds/struts-2.1.7.dtd">

1.4 完整代码

本案例web.xml完整代码:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>	
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 前端控制器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>
	org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

struts.xml完整代码:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
    "/struts.apache.org/dtds/struts-2.1.7.dtd">

2 Struts2之HelloWorld,Step5-7

2.1 问题

继续实现Struts2的HelloWorld示例,完成在Action的业务方法中输出“Hello,Action.”,在JSP页面上显示“Hello,Struts2.”。

2.2 方案

Struts2使用步骤:

  1. 编写业务控制器Action
  2. 编写JSP页面
  3. 配置struts.xml

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写业务控制器Action

创建名为action的包,并在该包下创建类HelloAction,如下图:

图-8

在HelloAction中,创建方法sayHello,该方法必须满足如下条件:

在该方法中输出“Hello,Action.”,并返回字符串”success”,代码如下:

package action;

public class HelloAction {

	/**
	 * 在业务方法中输出“Hello,Action.”
	 */
	public String sayHello() {
		System.out.println("Hello,Action.");
		return "success";
	}

}

步骤二:编写JSP页面

在WebRoot下,创建页面hello.jsp,如下图:

图-9

在页面上用标题显示出“Hello,Struts2.”。实现代码如下:

<%@page pageEncoding="utf-8" %>
<html>
<head>
</head>
<body>
	<h1>Hello,Struts2.</h1>
</body>
</html>

步骤三:配置struts.xml

在struts.xml中,配置Action及JSP。主要是配置请求找到Action的途径,以及Action跳转到JSP的途径,代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
    "/struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
#cold_bold	<!-- 
#cold_bold		package:包,用于对Action进行封装。
#cold_bold		1、name:包名,根元素下可以有多个包,彼此之间不能重名。
#cold_bold		2、extends:继承,用于指定继承的包,相当于将继承包下的配置信息复制到了当前包下。
#cold_bold		3、namespace:命名空间,用于规定Action的访问路径,必须以“/”开头。
#cold_bold		4、请求Action时,按照如下格式拼写URL:
#cold_bold		   /IP:PORT/ProjectName/NAMESPACE/ACTIONNAME.action
#cold_bold	-->
#cold_bold	<package name="day01" namespace="/demo" extends="struts-default">
#cold_bold		<!-- 
#cold_bold			action:业务控制器,用于注册业务控制器组件(类)。
#cold_bold			1、name:action名称,用于规定Action的访问路径。
#cold_bold				一个包下可以有多个action,彼此之间不能重名。
#cold_bold			2、class:业务控制器组件,用于指定业务控制器对应的类。
#cold_bold			3、method:方法,用于指定访问当前action时要调用的方法。
#cold_bold		 -->
#cold_bold		<action name="hello" class="action.HelloAction" method="sayHello">
#cold_bold			<!-- 
#cold_bold				result:输出组件,用于转发、重定向、直接输出。
#cold_bold				1、name:名称,一个action下可以有多个result,彼此之间不能重名。
#cold_bold				2、默认做转发,标记内容设置成转发的页面。
#cold_bold			 -->
#cold_bold			<result name="success">
#cold_bold				/hello.jsp
#cold_bold			</result>
#cold_bold		</action>
#cold_bold	</package>
</struts>

步骤四:测试

部署该项目并启动tomcat,打开浏览器,针对当前的案例,在地址栏中输入如下访问地址/localhost:8088/StrutsDay01/demo/hello.action。其中demo对应的是配置文件中的namespace值,hello对应的是配置文件中的action名称,“.action”是固定的后缀,也可以省略。

点击回车键,浏览器显示效果如下图:

/

图-10

同时,控制台输出内容如下图:

图-11

2.4 完整代码

本案例的web.xml完整代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<display-name></display-name>	
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 前端控制器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>
	org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

HelloAction完整代码:

package action;

public class HelloAction {

	/**
	 * 在业务方法中输出“Hello,Action.”
	 */
	public String sayHello() {
		System.out.println("Hello,Action.");
		return "success";
	}

}

struts.xml完整代码:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
    "/struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
	<!-- 
		package:包,用于对Action进行封装。
		1、name:包名,根元素下可以有多个包,彼此之间不能重名。
		2、extends:继承,用于指定继承的包,相当于将继承包下的配置信息复制到了当前包下。
		3、namespace:命名空间,用于规定Action的访问路径,必须以“/”开头。
		4、请求Action时,按照如下格式拼写URL:
		   /IP:PORT/ProjectName/NAMESPACE/ACTIONNAME.action
	-->
	<package name="day01" namespace="/demo" extends="struts-default">
		<!-- 
			action:业务控制器,用于注册业务控制器组件(类)。
			1、name:action名称,用于规定Action的访问路径。
				一个包下可以有多个action,彼此之间不能重名。
			2、class:业务控制器组件,用于指定业务控制器对应的类。
			3、method:方法,用于指定访问当前action时要调用的方法。
		 -->
		<action name="hello" class="action.HelloAction" method="sayHello">
			<!-- 
				result:输出组件,用于转发、重定向、直接输出。
				1、name:名称,一个action下可以有多个result,彼此之间不能重名。
				2、默认做转发,标记内容设置成转发的页面。
			 -->
			<result name="success">
				/hello.jsp
			</result>
		</action>
	</package>
</struts>

hello.jsp完整代码:

<%@page pageEncoding="utf-8" %>
<html>
<head>
</head>
<body>
	<h1>Hello,Struts2.</h1>
</body>
</html>

3 将页面表单中的数据提交给Action

3.1 问题

在Struts2框架下,如何将表单数据传递给业务控制器Action。

3.2 方案

Struts2中,表单向Action传递参数的方式有2种,并且这2种传参方式都是Struts2默认实现的,他们分别是基本属性注入、域模型注入。其中

我们可以使用项目StrutsDay01的HelloWorld示例,在其基础上练习使用这2种方式完成页面向Action的参数传递。具体的,我们可以在项目首页index.jsp上追加表单,并在表单中模拟一些数据,将这些数据提交给HelloAction,最后在HelloAction中将接收的参数输出到控制台。

3.3 步骤

1)基本属性注入

步骤一:追加表单,用于输入数据

在StrutsDay01项目的index.jsp中,追加表单,并将该表单设置提交给HelloAction,即将form的action属性设置为”/StrutsDay01/demo/hello.action”。

在表单中增加一个文本框,用于输入一个姓名,该文本框的name属性值为name。代码如下:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'index.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
</head>

<body>
    This is my JSP page. <br><br>

#cold_bold<form action="/StrutsDay01/demo/hello.action" method="post">
#cold_bold	<!--演示基本属性注入 -->
#cold_bold	姓名:<input type="text" name="realName"/><br/><br/>
#cold_bold	
#cold_bold	<input type="submit" value="提交"/>
#cold_bold</form>

</body>
</html>

步骤二:HelloAction中,接收表单传入的参数

在HelloAction中,追加属性用于接收表单传入的姓名参数,该属性的名称要求与文本框的name值相同(realName),并且该属性需要具备set方法。

在业务方法中输出属性realName的值。同时为了方便观察代码执行的顺序,建议在Action默认构造器中,输出任意的文字,代码如下:

package action;

public class HelloAction {

#cold_bold	public HelloAction() {
#cold_bold		System.out.println("实例化Action...");
#cold_bold	}

#cold_bold	// 定义基本类型属性,接收表单参数:姓名
#cold_bold	private String realName;
#cold_bold
#cold_bold	public void setRealName(String realName) {
#cold_bold		System.out.println("注入参数realName...");
#cold_bold		this.realName = realName;
#cold_bold	}
	
	/**
	 * 在业务方法中输出“Hello,Action.”
	 */
	public String sayHello() {
		System.out.println("Hello,Action.");

#cold_bold		// 输出基本类型数据
#cold_bold		System.out.println("姓名:" + realName);

		return "success";
	}

}

步骤三:测试

重新部署该项目并启动tomcat,打开浏览器,针对当前的案例,在地址栏中输入如下访问地址/localhost:8088/StrutsDay01。点击回车,页面显示出项目首页的内容,如下图:

图-12

在姓名文本框中输入任意内容(如Tarena),点击提交按钮,然后观察MyEclipse控制台输出的内容,如下图:

图-13

控制台输出的顺序可以证明代码的执行顺序为:实例化Action(调用set方法注入参数(调用业务方法,当然这个过程是Struts2的API自行实现的,我们只需要在写代码时满足上述步骤中的要求即可。

由于index.jsp中的表单将请求提交给HelloAction,而HelloAction又会跳转到hello.jsp页面,因此最终浏览器显示的效果如下图:

图-14

2)域模型注入

步骤一:修改表单,追加演示数据

修改表单,追加用户名、密码两个文本框,模拟输入用户的相关信息,代码如下:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'index.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
</head>

<body>
    This is my JSP page. <br><br>

<form action="/StrutsDay01/demo/hello.action" method="post">
	<!--演示基本属性注入 -->
	姓名:<input type="text" name="realName"/><br/><br/>
#cold_bold	<!--演示域模型注入 -->
#cold_bold	用户名:<input type="text" /><br/><br/>
#cold_bold	密码:<input type="password" /><br/><br/>
	
	<input type="submit" value="提交"/>
</form>

</body>
</html>

步骤二:创建实体类

创建包entity,用于存放实体类。在entity包下创建实体类User,用于封装表单中追加的数据,即用户名、密码。User中要包含2个属性,用于封装用户名、密码,并给属性提供get和set方法,代码如下:

package entity;

public class User {

	private String userName;// 用户名
	private String password;// 密码

	public String getPassword() {
		return password;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}

步骤三:修改HelloAction,接收表单传入的参数

在HelloAction中,追加属性用于接收表单传入的用户名、密码参数,该属性的类型为User类型,名称为user,并为该属性提供get和set方法。

在业务方法中输出属性user的值,代码如下:

package action;

import entity.User;

public class HelloAction {

	public HelloAction() {
		System.out.println("实例化Action...");
	}

	// 定义基本类型属性,接收表单参数:姓名
	private String realName;

	public void setRealName(String realName) {
		System.out.println("注入参数realName...");
		this.realName = realName;
	}
	
#cold_bold	//定义实体对象属性,接收表单参数:用户名、密码
#cold_bold	private User user;
#cold_bold
#cold_bold	public void setUser(User user) {
#cold_bold		System.out.println("注入对象user...");
#cold_bold		this.user = user;
#cold_bold	}
#cold_bold	
#cold_bold	public User getUser() {
#cold_bold		return this.user;
#cold_bold	}

	/**
	 * 在业务方法中输出“Hello,Action.”
	 */
	public String sayHello() {
		System.out.println("Hello,Action.");

		// 输出基本类型数据
		System.out.println("姓名:" + realName);
#cold_bold		// 输出域模型方式注入的参数
#cold_bold		System.out.println("用户名:" + user.getUserName());
#cold_bold		System.out.println("密码:" + user.getPassword());

		return "success";
	}

}

步骤四:修改表单,设置文本框属性

在index.jsp中,修改表单里新增的2个文本框的name属性值。对于域模型注入的方式,文本框name属性值应该是具有“对象名.属性名”格式的表达式。其中对象名指的是Action中的实体类型属性名,属性名指的是实体类型中的属性名,代码如下:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'index.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
</head>

<body>
    This is my JSP page. <br><br>

<form action="/StrutsDay01/demo/hello.action" method="post">
	<!--演示基本属性注入 -->
	姓名:<input type="text" name="realName"/><br/><br/>
#cold_bold	<!--演示域模型注入 -->
#cold_bold	用户名:<input type="text" name="user.userName"/><br/><br/>
#cold_bold	密码:<input type="text" name="user.password"/><br/><br/>
	
	<input type="submit" value="提交"/>
</form>

</body>
</html>

步骤五:测试

重新部署项目并启动tomcat,在浏览器中刷新地址/localhost:8088/StrutsDay01,效果如下图所示:

图-15

在文本框中输入任意的内容,点击提交,查看MyEclipse的控制台,输出结果如下图:

图-16

控制台输出的顺序可以证明代码的执行顺序为:实例化Action(实例化User并注入参数(调用set方法注入User对象(调用业务方法。

最终浏览器显示的效果如下图:

图-17

3.4 完整代码

本案例的完整代码如下。

index.jsp完整代码:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">

<title>My JSP 'index.jsp' starting page</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
</head>

<body>
    This is my JSP page. <br><br>

<form action="/StrutsDay01/demo/hello.action" method="post">
	<!-- 演示基本属性注入 -->
	姓名:<input type="text" name="realName"/><br/><br/>
	<!-- 演示域模型注入 -->
	用户名:<input type="text" name="user.userName"/><br/><br/>
	密码:<input type="text" name="user.password"/><br/><br/>
	
	<input type="submit" value="提交"/>
</form>

</body>
</html>

HelloAction完整代码:

package action;

import com.opensymphony.xwork2.ModelDriven;

import entity.User;

public class HelloAction {

	public HelloAction() {
		System.out.println("实例化Action...");
	}

	// 定义基本类型属性,接收表单参数:姓名
	private String realName;

	public void setRealName(String realName) {
		System.out.println("注入参数realName...");
		this.realName = realName;
	}
	
	//定义实体对象属性,接收表单参数:用户名、密码
	private User user;

	public void setUser(User user) {
		System.out.println("注入对象user...");
		this.user = user;
	}
	
	public User getUser() {
		return this.user;
	}

	/**
	 * 在业务方法中输出“Hello,Action.”
	 */
	public String sayHello() {
		System.out.println("Hello,Action.");

		// 输出基本类型数据
		System.out.println("姓名:" + realName);
		// 输出域模型方式注入的参数
		System.out.println("用户名:" + user.getUserName());
		System.out.println("密码:" + user.getPassword());

		return "success";
	}

}

User完整代码:

package entity;

public class User {

	private String userName;// 用户名
	private String password;// 密码

	public String getPassword() {
		return password;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}

其他代码没有变化,不再重复。

4 使用EL表达式,显示Action中的数据

4.1 问题

在Struts2框架下,如何将业务控制器Action的数据传递给JSP,并在JSP上显示出这些数据。

4.2 方案

Struts2会自动的将Action的数据传递给JSP,并且对传递方式进行了封装,在使用时变得十分方便,甚至不需要使用request对象。它仅仅要求我们在Action中定义属性,并为属性提供get方法,那么从Action跳转到JSP时Struts2会自动的通过这些get方法将这些属性的值传递给JSP。最终在JSP上我们可以使用EL表达式来显示Action的属性值。

我们还是利用StrutsDay01项目的HelloWorld示例,目前HelloAction中已经有了2个属性,即realName、user,而该Action最终跳转的页面为hello.jsp,我们的目标是在页面 hello.jsp上使用EL表达式输出这些属性值。

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:在Action中,给属性追加get方法

在HelloAction中,给realName属性追加get方法,用于页面的EL表达式访问并取值。user属性已经有了get方法,不再需要追加。代码如下:

package action;

import com.opensymphony.xwork2.ModelDriven;

import entity.User;

public class HelloAction {

	public HelloAction() {
		System.out.println("实例化Action...");
	}

	// 定义基本类型属性,接收表单参数:姓名
	private String realName;

	public void setRealName(String realName) {
		System.out.println("注入参数realName...");
		this.realName = realName;
	}
	
#cold_bold	public String getRealName() {
#cold_bold		return this.realName;
#cold_bold	}
	
	//定义实体对象属性,接收表单参数:用户名、密码
	private User user;

	public void setUser(User user) {
		System.out.println("注入对象user...");
		this.user = user;
	}
	
	public User getUser() {
		return this.user;
	}


	/**
	 * 在业务方法中输出“Hello,Action.”
	 */
	public String sayHello() {
		System.out.println("Hello,Action.");

		// 输出基本类型数据
		System.out.println("姓名:" + realName);
		// 输出域模型方式注入的参数
		System.out.println("用户名:" + user.getUserName());
		System.out.println("密码:" + user.getPassword());

		return "success";
	}

}

步骤二:在JSP上,使用EL表达式显示Action的属性值,代码如下:

<%@page pageEncoding="utf-8" isELIgnored="false"%>
<html>
<head>
</head>
<body>
	<h1>Hello,Struts2.</h1>
	
#cold_bold	<h1>姓名:${realName }</h1>
#cold_bold	
#cold_bold	<h1>用户名:${user.userName }</h1>
#cold_bold	<h1>密码:${user.password }</h1>
	
</body>
</html>

注意:如果使用的是tomcat5,那么它默认会忽略EL表达式,需要在page指令中设置不忽略,即isElIgnored=”false”。如果使用的是tomcat6或者更高版本,可以不进行此项设置。

步骤三:测试

重新部署项目并启动tomcat,在浏览器中刷新地址/localhost:8088/StrutsDay01,效果如下图所示:

图-18

在文本框中输入任意的内容,点击提交,此时表单数据提交给了HelloAction,HelloAction接收到了表单数据后,跳转到了hello.jsp,我们在hello.jsp上使用了EL表达式来输出HelloAction的属性值,效果如下图:

图-19

小结

Action中追加属性时,不必区别何时加get方法,何时加set方法,通常每个属性都有get和set方法。

页面上写的EL表达式,实际上与二种注入方式中,对应的表单文本框的name表达式写法一致。

4.4 完整代码

本案例的完整代码如下。

HelloAction完整代码:

package action;

import com.opensymphony.xwork2.ModelDriven;

import entity.User;

public class HelloAction {

	public HelloAction() {
		System.out.println("实例化Action...");
	}

	// 定义基本类型属性,接收表单参数:姓名
	private String realName;

	public void setRealName(String realName) {
		System.out.println("注入参数realName...");
		this.realName = realName;
	}
	
	public String getRealName() {
		return this.realName;
	}
	
	//定义实体对象属性,接收表单参数:用户名、密码
	private User user;

	public void setUser(User user) {
		System.out.println("注入对象user...");
		this.user = user;
	}
	
	public User getUser() {
		return this.user;
	}
	

	/**
	 * 在业务方法中输出“Hello,Action.”
	 */
	public String sayHello() {
		System.out.println("Hello,Action.");

		// 输出基本类型数据
		System.out.println("姓名:" + realName);
		// 输出域模型方式注入的参数
		System.out.println("用户名:" + user.getUserName());
		System.out.println("密码:" + user.getPassword());
		//输出模型驱动方式注入的参数
		System.out.println("员工名:" + emp.getEmpName());
		System.out.println("工资:" + emp.getSalary());

		return "success";
	}

}

hello.jsp完整代码:

<%@page pageEncoding="utf-8" %>
<html>
<head>
</head>
<body>
	<h1>Hello,Struts2.</h1>
	
	<h1>姓名:${realName }</h1>
	
	<h1>用户名:${user.userName }</h1>
	<h1>密码:${user.password }</h1>
	
</body>
</html>

其他代码没有变化,不再重复。

5 NetCTOSS资费列表,Step1-3

5.1 问题

开发NetCTOSS项目中,资费模块的查询功能。要求查询全部的资费表数据并以列表的方式显示在页面上,其中分页功能先不实现。具体效果如下图:

图-20

5.2 方案

查询功能只包含一个请求,即对资费数据的查询请求。在这次请求中,我们需要查询出资费所有的数据,然后将数据传递给查询列表页面,在页面上用表格显示出来。

使用Struts2处理这种请求的过程如下图:

图-21

在这个请求的过程中,我们需要编写的代码有:

这些代码的编写顺序,往往是按照他们的相互依赖关系来进行的,优先编写被依赖的代码,因此推荐代码编写的顺序是Entity(DAO(Action(struts.xml(JSP。由于步骤比较多,我们分2次来完成。本案例中我们先完成Entity、DAO代码的编写。

需要注意如下两点:

1、struts.xml写在JSP前面的原因是,开发完该配置文件,可以部署并启动项目,而在开发JSP时可以一边写一边刷新看效果,不必再重新启动服务了。

2、NetCTOSS项目我们采用ORACLE数据库开发。

5.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:项目搭建

项目搭建包括项目的创建以及引入框架等操作,目前NetCTOSS项目仅采用了Struts2的框架,搭建过程只包含Struts2框架的引入即可,因此这一步可以参考今天的第一个案例,即“Struts2之HelloWorld,Step1-4”。

项目名称定为NETCTOSS,后续我们会陆续完成项目的各个功能,代码都将写在这个项目下。项目搭建后结构如下图:

图-22

步骤二:Entity

创建com.netctoss.entity包,用于封装实体类。然后创建资费表对应的实体类Cost,代码如下:

package com.netctoss.entity;

import java.sql.Date;

/**
 * 资费实体类
 */
public class Cost {

	private Integer id;// 主键
	private String name;// 资费名称
	private Integer baseDuration;// 在线时长
	private Double baseCost;// 基本费用
	private Double unitCost;// 单位费用
	private String status;// 状态
	private String descr;// 资费说明
	private Date createTime;// 创建日期
	private Date startTime;// 启用日期
	private String costType;// 资费类型

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getBaseDuration() {
		return baseDuration;
	}

	public void setBaseDuration(Integer baseDuration) {
		this.baseDuration = baseDuration;
	}

	public Double getBaseCost() {
		return baseCost;
	}

	public void setBaseCost(Double baseCost) {
		this.baseCost = baseCost;
	}

	public Double getUnitCost() {
		return unitCost;
	}

	public void setUnitCost(Double unitCost) {
		this.unitCost = unitCost;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public String getDescr() {
		return descr;
	}

	public void setDescr(String descr) {
		this.descr = descr;
	}

	public Date getCreateTime() {
		return createTime;
	}

	public Date getStartTime() {
		return startTime;
	}

	public void setStartTime(Date startTime) {
		this.startTime = startTime;
	}

	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}

	public String getCostType() {
		return costType;
	}

	public void setCostType(String costType) {
		this.costType = costType;
	}

}

步骤三:DAO

DAO是数据库访问组件,用于封装对数据库的增、删、改、查操作。当前请求中查询全部资费数据的方法,需要写在DAO中。

首先,我们要创建DAO组件并编写本案例中需要使用的查询全部资费数据的代码。但是通常情况下,我们并不是直接写一个DAO的类,在类中直接编写这样的代码,而是先定义接口。

定义接口的原因在于,一般情况下业务是比较固定的,针对这些业务我们需要提供的数据库访问方法也相对固定,而通过接口我们是可以描述出这些固定的业务。那么定义接口的目的在于,实际项目中,随着技术的革新和性能上的要求,我们可能会改变实现这种业务的手段,比如当前我们使用JDBC来实现DAO,可能过了一段时间,我们又想使用Hibernate替换JDBC来实现DAO。

对于这种场景,如果DAO仅仅是一个类,直接实现业务,那么业务代码中会大量的调用它,我们很难再去进行修改了。而使用接口的话,我们业务代码中可以通过工厂(后面会说明如何使用工厂实例化接口)调用接口,修改时只需要重新写个实现类,来替换工厂中原有的实现类即可,对业务代码没有任何的影响。简单来说,定义接口是为了提高代码的复用性,降低代码维护的成本。

因此我们需要创建一个DAO接口,并定义查询全部资费的方法,代码如下:

package com.netctoss.dao;

import java.util.List;

import com.netctoss.entity.Cost;

/**
 * 资费DAO接口
 */
public interface ICostDao {

	/**
	 * 查询全部资费数据
	 */
	List<Cost>findAll();

}

然后,要实现这个接口,目前我们可以使用JDBC/MyBatis来访问数据库,实现这个接口,由于本案例侧重点在Struts2的使用,因此就简化DAO的实现,这里简单的模拟一下这个查询方法的实现。即创建一个实现类CostDaoImpl实现接口ICostDao,并使用模拟的方式来实现查询方法,代码如下:

package com.netctoss.dao;

import java.util.ArrayList;
import java.util.List;

import com.netctoss.entity.Cost;

/**
 *	当前阶段学习重点是Struts2,对于DAO的实现就模拟实现了。
 *	同学们可以使用JDBC/MyBatis自行实现该DAO。
 */
public class CostDaoImpl implements ICostDao {

	@Override
	public List<Cost> findAll() {
		// 模拟查询全部资费数据
		List<Cost> list = new ArrayList<Cost>();
		
		Cost c1 = new Cost();
		c1.setId(95);
		c1.setName("6元套餐");
		c1.setBaseDuration(66);
		c1.setBaseCost(6.6);
		c1.setUnitCost(0.6);
		c1.setDescr("6元套餐");
		c1.setStatus("0");
		c1.setCostType("2");
		list.add(c1);
		
		Cost c2 = new Cost();
		c2.setId(96);
		c2.setName("8元套餐");
		c2.setBaseDuration(88);
		c2.setBaseCost(8.8);
		c2.setUnitCost(0.8);
		c2.setDescr("8元套餐");
		c2.setStatus("0");
		c2.setCostType("2");
		list.add(c2);
		
		Cost c3 = new Cost();
		c3.setId(97);
		c3.setName("tarena");
		c3.setBaseDuration(99);
		c3.setBaseCost(9.9);
		c3.setUnitCost(0.9);
		c3.setDescr("tarena套餐");
		c3.setStatus("0");
		c3.setCostType("2");
		list.add(c3);
		
		return list;
	}

5.4 完整代码

本案例的完整代码如下。

Entity完整代码:

package com.netctoss.entity;

import java.sql.Date;

/**
 * 资费实体类
 */
public class Cost {

	private Integer id;// 主键
	private String name;// 资费名称
	private Integer baseDuration;// 在线时长
	private Double baseCost;// 基本费用
	private Double unitCost;// 单位费用
	private String status;// 状态
	private String descr;// 资费说明
	private Date createTime;// 创建日期
	private Date startTime;// 启用日期
	private String costType;// 资费类型

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getBaseDuration() {
		return baseDuration;
	}

	public void setBaseDuration(Integer baseDuration) {
		this.baseDuration = baseDuration;
	}

	public Double getBaseCost() {
		return baseCost;
	}

	public void setBaseCost(Double baseCost) {
		this.baseCost = baseCost;
	}

	public Double getUnitCost() {
		return unitCost;
	}

	public void setUnitCost(Double unitCost) {
		this.unitCost = unitCost;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public String getDescr() {
		return descr;
	}

	public void setDescr(String descr) {
		this.descr = descr;
	}

	public Date getCreateTime() {
		return createTime;
	}

	public Date getStartTime() {
		return startTime;
	}

	public void setStartTime(Date startTime) {
		this.startTime = startTime;
	}

	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}

	public String getCostType() {
		return costType;
	}

	public void setCostType(String costType) {
		this.costType = costType;
	}

}

DAO接口完整代码:

package com.netctoss.dao;

import java.util.List;

import com.netctoss.entity.Cost;

/**
 * 资费DAO接口
 */
public interface ICostDao {

	/**
	 * 查询全部资费数据
	 */
	List<Cost>findAll();

}

DAO实现类完整代码:

package com.netctoss.dao;

import java.util.ArrayList;
import java.util.List;

import com.netctoss.entity.Cost;

/**
 *	当前阶段学习重点是Struts2,对于DAO的实现就模拟实现了。
 *	同学们可以使用JDBC/MyBatis自行实现该DAO。
 */
public class CostDaoImpl implements ICostDao {

	@Override
	public List<Cost> findAll() {
		// 模拟查询全部资费数据
		List<Cost> list = new ArrayList<Cost>();
		
		Cost c1 = new Cost();
		c1.setId(95);
		c1.setName("6元套餐");
		c1.setBaseDuration(66);
		c1.setBaseCost(6.6);
		c1.setUnitCost(0.6);
		c1.setDescr("6元套餐");
		c1.setStatus("0");
		c1.setCostType("2");
		list.add(c1);
		
		Cost c2 = new Cost();
		c2.setId(96);
		c2.setName("8元套餐");
		c2.setBaseDuration(88);
		c2.setBaseCost(8.8);
		c2.setUnitCost(0.8);
		c2.setDescr("8元套餐");
		c2.setStatus("0");
		c2.setCostType("2");
		list.add(c2);
		
		Cost c3 = new Cost();
		c3.setId(97);
		c3.setName("tarena");
		c3.setBaseDuration(99);
		c3.setBaseCost(9.9);
		c3.setUnitCost(0.9);
		c3.setDescr("tarena套餐");
		c3.setStatus("0");
		c3.setCostType("2");
		list.add(c3);
		
		return list;
	}

6 NetCTOSS资费列表,Step4-6

6.1 问题

继续第5个案例,NetCTOSS资费列表。

6.2 方案

本案例中,我们要完成Action、struts.xml、JSP代码的编写。

6.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:Action

Action是业务控制器,需要调用DAO组织业务流程,并将计算出的结果传递给页面显示。其中对DAO的调用,不要直接在Action中实例化CostDaoImpl,避免业务代码中大量的引用该实现类,如将来更换实现DAO方式时不好修改。

这种情况,我们通常情况是使用工厂类来创建DAO的实例,并返回其接口类型即可。那么Action中通过工厂获取到接口,其依赖的仅仅是这个接口,当DAO实现方式变更时,只需要修改工厂即可,项目中所有的Action类都不需要做任何的修改。

因此,首先我们创建这个工厂类,使用工厂方法来创建DAO的实例,即在com.netctoss.dao的包下创建工厂类DAOFactory,代码如下:

package com.netctoss.dao;

/**
 * DAO工厂,负责统一的创建DAO实例。
 */
public class DAOFactory {

	/**
	 * 资费DAO实例
	 */
	private static ICostDao costDao = new CostDaoImpl();

	/**
	 * 返回资费DAO实例
	 */
	public static ICostDao getCostDAO() {
		returncostDao;
	}

}

接下来,我们要创建业务控制器Action,并在其中处理查询请求。即创建包com.netctoss.action,用于封装所有的Action类,然后在此包下创建类FindCostAction,并在Action的业务方法中调用DAO查询出所有的资费,然后通过属性将结果传递给JSP。代码如下:

package com.netctoss.action;

import java.util.List;
import com.netctoss.dao.DAOFactory;
import com.netctoss.dao.ICostDao;
import com.netctoss.entity.Cost;

/**
 *	资费查询Action
 */
public class FindCostAction {

	// output
	private List<Cost> costs;

	/**
	 * 业务方法,配置action时如果不指定method属性,
	 * 那么Struts2会自动调用execute方法。
	 */
	public String execute() {
		// 获取DAO实例
		ICostDao dao = DAOFactory.getCostDAO();
		// 调用DAO,计算输出
		try {
			costs = dao.findAll();
		} catch (Exception e) {
			e.printStackTrace();
			// 捕获到异常时,跳转到错误页面
			return "error";
		}
		// 正常执行时,跳转到资费列表页面
		return "success";
	}

	public List<Cost> getCosts() {
		return costs;
	}

	public void setCosts(List<Cost> costs) {
		this.costs = costs;
	}

}

注意:Action是业务控制器,它的作用是根据用户的输入来计算输出,并将结果返回给页面显示给用户看,简单说就是根据输入计算输出。因此在实现Action时,我们首先分析当前请求的输入和输出分别是什么,然后在Action中定义成员变量来封装输入和输出,之后在业务方法中根据输入属性计算输出属性的值即可。

步骤二:struts.xml

在struts.xml下,配置action以及JSP。由于目前还没有创建JSP,因此我们先预想好JSP的名称加以配置,后面创建JSP时要使用配置中指定的名称,代码如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
    "/struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
	
	<!-- 
		资费模块配置信息:
		一般情况下,一个模块的配置单独封装在一个package下,
		并且以模块名来命名package的name和namespace。
	 -->
	<package name="cost" namespace="/cost" extends="struts-default">
		<!--查询资费数据 -->
		<action name="findCost" class="com.netctoss.action.FindCostAction">
			<!-- 
				正常情况下跳转到资费列表页面。
				一般一个模块的页面要打包在一个文件夹下,并且文件夹以模块名命名。
			 -->
			<result name="success">
				/WEB-INF/cost/find_cost.jsp
			</result>
			<!-- 
				错误情况下,跳转到错误页面。
				错误页面可以被所有模块复用,因此放在main下,
				该文件夹用于存放公用的页面。
			 -->
			<result name="error">
				/WEB-INF/main/error.jsp
			</result>
		</action>
	</package>
	
</struts>

步骤三:JSP

资费列表页面需要依赖样式文件,因此首先将样式文件及图片复制到项目中来,即将静态项目NETCTOSS_V02中images和styles文件夹复制到NetCTOSS项目的WebRoot文件夹下,完成后WebRoot结构如下图:

图-23

接下来,在WEB-INF下创建文件夹cost,并在该文件夹下创建find_cost.jsp。在该JSP中,我们先指定文件的编码为utf-8,然后从静态代码NETCTOSS_V02中,将资费列表的HTML代码(/fee/fee_list.html)复制到JSP内,完成后代码如下:

<%@page pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "/www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="/www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" />
<link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" />
<script language="javascript" type="text/javascript">
            //排序按钮的点击事件
function sort(btnObj) {
if (btnObj.className == "sort_desc")
                    btnObj.className = "sort_asc";
else
                    btnObj.className = "sort_desc";
            }

            //启用
function startFee() {
                var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。");
document.getElementById("operate_result_info").style.display = "block";
            }
            //删除
function deleteFee() {
                var r = window.confirm("确定要删除此资费吗?");
document.getElementById("operate_result_info").style.display = "block";
            }
</script>
</head>
<body>
<!--Logo区域开始-->
<div id="header">
<img src="../images/logo.png" alt="logo" class="left"/>
<a href="#">[退出]</a>
</div>
<!--Logo区域结束-->
<!--导航区域开始-->
<div id="navi">
<ul id="menu">
<li><a href="../index.html" class="index_off"></a></li>
<li><a href="../role/role_list.html" class="role_off"></a></li>
<li><a href="../admin/admin_list.html" class="admin_off"></a></li>
<li><a href="../fee/fee_list.html" class="fee_on"></a></li>
<li><a href="../account/account_list.html" class="account_off"></a></li>
<li><a href="../service/service_list.html" class="service_off"></a></li>
<li><a href="../bill/bill_list.html" class="bill_off"></a></li>
<li><a href="../report/report_list.html" class="report_off"></a></li>
<li><a href="../user/user_info.html" class="information_off"></a></li>
<li><a href="../user/user_modi_pwd.html" class="password_off"></a></li>
</ul>
</div>
<!--导航区域结束-->
<!--主要区域开始-->
<div id="main">
<form action="" method="">
<!--排序-->
<div class="search_add">
<div>
<!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />-->
<input type="button" value="基费" class="sort_asc" onclick="sort(this);" />
<input type="button" value="时长" class="sort_asc" onclick="sort(this);" />
</div>
<input type="button" value="增加" class="btn_add" onclick="location.href='fee_add.html';" />
</div>
<!--启用操作的操作提示-->
<div id="operate_result_info" class="operate_success">
<img src="../images/close.png" onclick="this.parentNode.style.display='none';" />
删除成功!
</div>
<!--数据区域:用表格展示数据-->
<div id="data">
<table id="datalist">
<tr>
<th>资费ID</th>
<th class="width100">资费名称</th>
<th>基本时长</th>
<th>基本费用</th>
<th>单位费用</th>
<th>创建时间</th>
<th>开通时间</th>
<th class="width50">状态</th>
<th class="width200"></th>
</tr>
<tr>
<td>1</td>
<td><a href="fee_detail.html">包 20 小时</a></td>
<td>20 小时</td>
<td>24.50 元</td>
<td>3.00 元/小时</td>
<td>2013/01/01 00:00:00</td>
<td></td>
<td>暂停</td>
<td>
<input type="button" value="启用" class="btn_start" onclick="startFee();" />
<input type="button" value="修改" class="btn_modify" onclick="location.href='fee_modi.html';" />
<input type="button" value="删除" class="btn_delete" onclick="deleteFee();" />
</td>
</tr>
<tr>
<td>2</td>
<td><a href="fee_detail.html">包 40 小时</a></td>
<td>40 小时</td>
<td>40.50 元</td>
<td>3.00 元/小时</td>
<td>2013/01/21 00:00:00</td>
<td>2013/01/23 00:00:00</td>
<td>开通</td>
<td>
</td>
</tr>
</table>
<p>业务说明:<br />
                    1、创建资费时,状态为暂停,记载创建时间;<br />
                    2、暂停状态下,可修改,可删除;<br />
                    3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br />
                    4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理)
</p>
</div>
<!--分页-->
<div id="pages">
	<a href="#">上一页</a>
<a href="#" class="current_page">1</a>
<a href="#">2</a>
<a href="#">3</a>
<a href="#">4</a>
<a href="#">5</a>
<a href="#">下一页</a>
</div>
</form>
</div>
<!--主要区域结束-->
<div id="footer">
<p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p>
<p>版权所有(C)</p>
</div>
</body>
</html>

然后,在WEB-INF下创建文件夹main,并在该文件夹下创建公用的错误页面error.jsp。在该JSP中,我们先指定文件的编码为utf-8,然后从静态项目NETCTOSS_V02中,将错误页面的HTML代码(error.html)复制到JSP内,完成后代码如下:

<%@page pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "/www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="/www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" />
<link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" />
<script language="javascript" type="text/javascript">
var timer;
            //启动跳转的定时器
function startTimes() {
timer = window.setInterval(showSecondes,1000);
            }

var i = 5;
function showSecondes() {
if (i > 0) {
i--;
document.getElementById("secondes").innerHTML = i;
                }
else {
window.clearInterval(timer);
                    location.href = "login.html";
                }
            }

            //取消跳转
function resetTimer() {
if (timer != null && timer != undefined) {
window.clearInterval(timer);
                    location.href = "login.html";
                }
            }
</script>
</head>
<body class="error_page" onload="startTimes();">
<h1 id="error">
	遇到错误,&nbsp;<span id="secondes">5</span>&nbsp;秒后将自动跳转,立即跳转请点击&nbsp;
<a  href="javascript:resetTimer();">返回</a>
</h1>
</body>
</html>

完成这一步后,我们先做一下阶段性测试,以验证是否能通过FindCostAction访问到find_cost.jsp。即部署NetCTOSS项目,然后启动tomcat,在浏览器地址栏中输入/localhost:8088/NETCTOSS/cost/findCost,点击回车,效果如下图:

图-24

最后,我们需要修改find_cost.jsp,将中间的表格区域静态的数据改为动态的数据显示,其数据来源于FindCostAction。

对于页面上这份集合数据的动态显示,我们需要使用JSTL标签+EL表达式来实现,因此需要先引入JSTL的包,即jstl.jar、standard.jar。引入他们之后,项目中的lib包结构如下图:

图-25

然后,我们要在find_cost.jsp中通过taglib指令引入JSTL标签,并设置不忽略EL表达式(tomcat6及以上版本不需要设置),相关代码如下:

<%@page pageEncoding="utf-8" isELIgnored="false"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>

最后,我们使用JSTL标签和EL表达式来动态显示页面表格中的数据,代码如下:

#cold_bold<%@page pageEncoding="utf-8" isELIgnored="false"%>
#cold_bold<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "/www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="/www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" />
<link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" />
<script language="javascript" type="text/javascript">
            //排序按钮的点击事件
function sort(btnObj) {
if (btnObj.className == "sort_desc")
                    btnObj.className = "sort_asc";
else
                    btnObj.className = "sort_desc";
            }

            //启用
function startFee() {
                var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。");
document.getElementById("operate_result_info").style.display = "block";
            }
            //删除
function deleteFee() {
                var r = window.confirm("确定要删除此资费吗?");
document.getElementById("operate_result_info").style.display = "block";
            }
</script>
</head>
<body>
<!--Logo区域开始-->
<div id="header">
<img src="../images/logo.png" alt="logo" class="left"/>
<a href="#">[退出]</a>
</div>
<!--Logo区域结束-->
<!--导航区域开始-->
<div id="navi">
<ul id="menu">
<li><a href="../index.html" class="index_off"></a></li>
<li><a href="../role/role_list.html" class="role_off"></a></li>
<li><a href="../admin/admin_list.html" class="admin_off"></a></li>
<li><a href="../fee/fee_list.html" class="fee_on"></a></li>
<li><a href="../account/account_list.html" class="account_off"></a></li>
<li><a href="../service/service_list.html" class="service_off"></a></li>
<li><a href="../bill/bill_list.html" class="bill_off"></a></li>
<li><a href="../report/report_list.html" class="report_off"></a></li>
<li><a href="../user/user_info.html" class="information_off"></a></li>
<li><a href="../user/user_modi_pwd.html" class="password_off"></a></li>
</ul>
</div>
<!--导航区域结束-->
<!--主要区域开始-->
<div id="main">
<form action="" method="">
<!--排序-->
<div class="search_add">
<div>
<!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />-->
<input type="button" value="基费" class="sort_asc" onclick="sort(this);" />
<input type="button" value="时长" class="sort_asc" onclick="sort(this);" />
</div>
<input type="button" value="增加" class="btn_add" onclick="location.href='fee_add.html';" />
</div>
<!--启用操作的操作提示-->
<div id="operate_result_info" class="operate_success">
<img src="../images/close.png" onclick="this.parentNode.style.display='none';" />
删除成功!
</div>
<!--数据区域:用表格展示数据-->
<div id="data">
<table id="datalist">
<tr>
<th>资费ID</th>
<th class="width100">资费名称</th>
<th>基本时长</th>
<th>基本费用</th>
<th>单位费用</th>
<th>创建时间</th>
<th>开通时间</th>
<th class="width50">状态</th>
<th class="width200"></th>
</tr>

#cold_bold<!--使用JSTL标签遍历集合,使用EL表达式输出内容。 -->
#cold_bold<c:forEach items="${costs}" var="cost">
#cold_bold	<tr>
#cold_bold	<td>${cost.id}</td>
#cold_bold	<td><a href="fee_detail.html">${cost.name}</a></td>
#cold_bold	<td>${cost.baseDuration}</td>
#cold_bold	<td>${cost.baseCost}</td>
#cold_bold	<td>${cost.unitCost}</td>
#cold_bold	<td>${cost.createTime}</td>
#cold_bold	<td>${cost.startTime}</td>
#cold_bold	<td>
#cold_bold		<c:choose>
#cold_bold			<c:when test="${cost.status==0}">开通</c:when>
#cold_bold			<c:otherwise>暂停</c:otherwise>
#cold_bold		</c:choose>
#cold_bold	</td>
#cold_bold	<td>
#cold_bold	<input type="button" value="启用" class="btn_start" onclick="startFee();" />
#cold_bold	<input type="button" value="修改" class="btn_modify" onclick="location.href='fee_modi.html';" />
#cold_bold	<input type="button" value="删除" class="btn_delete" onclick="deleteFee();" />
#cold_bold	</td>
#cold_bold	</tr>
#cold_bold</c:forEach>

</table>
<p>业务说明:<br />
                    1、创建资费时,状态为暂停,记载创建时间;<br />
                    2、暂停状态下,可修改,可删除;<br />
                    3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br />
                    4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理)
</p>
</div>
<!--分页-->
<div id="pages">
	<a href="#">上一页</a>
<a href="#" class="current_page">1</a>
<a href="#">2</a>
<a href="#">3</a>
<a href="#">4</a>
<a href="#">5</a>
<a href="#">下一页</a>
</div>
</form>
</div>
<!--主要区域结束-->
<div id="footer">
<p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p>
<p>版权所有(C)</p>
</div>
</body>
</html>

步骤四:单元测试

重新访问此功能,根据页面显示的数据测试代码是否正确。即在浏览器中刷新地址/localhost:8088/NETCTOSS/cost/findCost,页面效果如下图:

图-26

6.4 完整代码

本案例的完整代码如下。

DAOFactory完整代码:

package com.netctoss.dao;

/**
 * DAO工厂,负责统一的创建DAO实例。
 */
public class DAOFactory {

	/**
	 * 资费DAO实例
	 */
	private static ICostDao costDao = new CostDaoImpl();

	/**
	 * 返回资费DAO实例
	 */
	public static ICostDao getCostDAO() {
		returncostDao;
	}

}

FindCostAction完整代码:

package com.netctoss.action;

import java.util.List;
import com.netctoss.dao.DAOFactory;
import com.netctoss.dao.ICostDao;
import com.netctoss.entity.Cost;

/**
 *	资费查询Action
 */
public class FindCostAction {

	// output
	private List<Cost> costs;

	/**
	 * 业务方法,配置action时如果不指定method属性,
	 * 那么Struts2会自动调用execute方法。
	 */
	public String execute() {
		// 获取DAO实例
		ICostDao dao = DAOFactory.getCostDAO();
		// 调用DAO,计算输出
		try {
			costs = dao.findAll();
		} catch (Exception e) {
			e.printStackTrace();
			// 捕获到异常时,跳转到错误页面
			return "error";
		}
		// 正常执行时,跳转到资费列表页面
		return "success";
	}

	public List<Cost> getCosts() {
		return costs;
	}

	public void setCosts(List<Cost> costs) {
		this.costs = costs;
	}

}

struts.xml完整代码:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN"
    "/struts.apache.org/dtds/struts-2.1.7.dtd">
<struts>
	
	<!-- 
		资费模块配置信息:
		一般情况下,一个模块的配置单独封装在一个package下,
		并且以模块名来命名package的name和namespace。
	 -->
	<package name="cost" namespace="/cost" extends="struts-default">
		<!-- 查询资费数据 -->
		<action name="findCost" class="com.netctoss.action.FindCostAction">
			<!-- 
				正常情况下跳转到资费列表页面。
				一般一个模块的页面要打包在一个文件夹下,并且文件夹以模块名命名。
			 -->
			<result name="success">
				/WEB-INF/cost/find_cost.jsp
			</result>
			<!-- 
				错误情况下,跳转到错误页面。
				错误页面可以被所有模块复用,因此放在main下,
				该文件夹用于存放公用的页面。
			 -->
			<result name="error">
				/WEB-INF/main/error.jsp
			</result>
		</action>
	</package>
	
</struts>

find_cost.jsp完整代码:

<%@page pageEncoding="utf-8" isELIgnored="false"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "/www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="/www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" />
<link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" />
<script language="javascript" type="text/javascript">
            //排序按钮的点击事件
function sort(btnObj) {
if (btnObj.className == "sort_desc")
                    btnObj.className = "sort_asc";
else
                    btnObj.className = "sort_desc";
            }

            //启用
function startFee() {
                var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。");
document.getElementById("operate_result_info").style.display = "block";
            }
            //删除
function deleteFee() {
                var r = window.confirm("确定要删除此资费吗?");
document.getElementById("operate_result_info").style.display = "block";
            }
</script>
</head>
<body>
<!--Logo区域开始-->
<div id="header">
<img src="../images/logo.png" alt="logo" class="left"/>
<a href="#">[退出]</a>
</div>
<!--Logo区域结束-->
<!--导航区域开始-->
<div id="navi">
<ul id="menu">
<li><a href="../index.html" class="index_off"></a></li>
<li><a href="../role/role_list.html" class="role_off"></a></li>
<li><a href="../admin/admin_list.html" class="admin_off"></a></li>
<li><a href="../fee/fee_list.html" class="fee_on"></a></li>
<li><a href="../account/account_list.html" class="account_off"></a></li>
<li><a href="../service/service_list.html" class="service_off"></a></li>
<li><a href="../bill/bill_list.html" class="bill_off"></a></li>
<li><a href="../report/report_list.html" class="report_off"></a></li>
<li><a href="../user/user_info.html" class="information_off"></a></li>
<li><a href="../user/user_modi_pwd.html" class="password_off"></a></li>
</ul>
</div>
<!--导航区域结束-->
<!--主要区域开始-->
<div id="main">
<form action="" method="">
<!--排序-->
<div class="search_add">
<div>
<!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />-->
<input type="button" value="基费" class="sort_asc" onclick="sort(this);" />
<input type="button" value="时长" class="sort_asc" onclick="sort(this);" />
</div>
<input type="button" value="增加" class="btn_add" onclick="location.href='fee_add.html';" />
</div>
<!--启用操作的操作提示-->
<div id="operate_result_info" class="operate_success">
<img src="../images/close.png" onclick="this.parentNode.style.display='none';" />
                    删除成功!
</div>
<!--数据区域:用表格展示数据-->
<div id="data">
<table id="datalist">
<tr>
<th>资费ID</th>
<th class="width100">资费名称</th>
<th>基本时长</th>
<th>基本费用</th>
<th>单位费用</th>
<th>创建时间</th>
<th>开通时间</th>
<th class="width50">状态</th>
<th class="width200"></th>
</tr>

<!-- 使用JSTL标签遍历集合,使用EL表达式输出内容。 -->
<c:forEach items="${costs}" var="cost">
	<tr>
	<td>${cost.id}</td>
	<td><a href="fee_detail.html">${cost.name}</a></td>
	<td>${cost.baseDuration}</td>
	<td>${cost.baseCost}</td>
	<td>${cost.unitCost}</td>
	<td>${cost.createTime}</td>
	<td>${cost.startTime}</td>
	<td>
		<c:choose>
			<c:when test="${cost.status==0}">开通</c:when>
			<c:otherwise>暂停</c:otherwise>
		</c:choose>
	</td>
	<td>
	<input type="button" value="启用" class="btn_start" onclick="startFee();" />
	<input type="button" value="修改" class="btn_modify" onclick="location.href='fee_modi.html';" />
	<input type="button" value="删除" class="btn_delete" onclick="deleteFee();" />
	</td>
	</tr>
</c:forEach>

</table>
<p>业务说明:<br />
                    1、创建资费时,状态为暂停,记载创建时间;<br />
                    2、暂停状态下,可修改,可删除;<br />
                    3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br />
                    4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理)
</p>
</div>
<!--分页-->
<div id="pages">
	<a href="#">上一页</a>
<a href="#" class="current_page">1</a>
<a href="#">2</a>
<a href="#">3</a>
<a href="#">4</a>
<a href="#">5</a>
<a href="#">下一页</a>
</div>
</form>
</div>
<!--主要区域结束-->
<div id="footer">
<p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p>
<p>版权所有(C) </p>
</div>
</body>
</html>

error.jsp完整代码:

<%@page pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "/www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="/www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" />
<link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" />
<script language="javascript" type="text/javascript">
var timer;
            //启动跳转的定时器
function startTimes() {
timer = window.setInterval(showSecondes,1000);
            }

var i = 5;
function showSecondes() {
if (i > 0) {
i--;
document.getElementById("secondes").innerHTML = i;
                }
else {
window.clearInterval(timer);
                    location.href = "login.html";
                }
            }

            //取消跳转
function resetTimer() {
if (timer != null && timer != undefined) {
window.clearInterval(timer);
                    location.href = "login.html";
                }
            }
</script>
</head>
<body class="error_page" onload="startTimes();">
<h1 id="error">
	        遇到错误,&nbsp;<span id="secondes">5</span>&nbsp;秒后将自动跳转,立即跳转请点击&nbsp;
<a  href="javascript:resetTimer();">返回</a>
</h1>
</body>
</html>