使用Struts2框架,写一个HelloWorld示例。其中,在Action的业务方法中输出“Hello,Action.”,在JSP页面上显示“Hello,Struts2.”。
本案例要求完成Struts2使用步骤1-4,来构建起Struts2使用的准备环境。
Struts2使用步骤:
实现此案例需要按照如下步骤进行。
步骤一:创建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="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://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" "http://struts.apache.org/dtds/struts-2.1.7.dtd">
本案例web.xml完整代码:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://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" "http://struts.apache.org/dtds/struts-2.1.7.dtd">
继续实现Struts2的HelloWorld示例,完成在Action的业务方法中输出“Hello,Action.”,在JSP页面上显示“Hello,Struts2.”。
Struts2使用步骤:
实现此案例需要按照如下步骤进行。
步骤一:编写业务控制器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" "http://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 http://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,打开浏览器,针对当前的案例,在地址栏中输入如下访问地址http://localhost:8088/StrutsDay01/demo/hello.action。其中demo对应的是配置文件中的namespace值,hello对应的是配置文件中的action名称,“.action”是固定的后缀,也可以省略。
点击回车键,浏览器显示效果如下图:
/
图-10
同时,控制台输出内容如下图:
图-11
本案例的web.xml完整代码如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://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" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- package:包,用于对Action进行封装。 1、name:包名,根元素下可以有多个包,彼此之间不能重名。 2、extends:继承,用于指定继承的包,相当于将继承包下的配置信息复制到了当前包下。 3、namespace:命名空间,用于规定Action的访问路径,必须以“/”开头。 4、请求Action时,按照如下格式拼写URL: http://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>
在Struts2框架下,如何将表单数据传递给业务控制器Action。
Struts2中,表单向Action传递参数的方式有2种,并且这2种传参方式都是Struts2默认实现的,他们分别是基本属性注入、域模型注入。其中
我们可以使用项目StrutsDay01的HelloWorld示例,在其基础上练习使用这2种方式完成页面向Action的参数传递。具体的,我们可以在项目首页index.jsp上追加表单,并在表单中模拟一些数据,将这些数据提交给HelloAction,最后在HelloAction中将接收的参数输出到控制台。
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,打开浏览器,针对当前的案例,在地址栏中输入如下访问地址http://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,在浏览器中刷新地址http://localhost:8088/StrutsDay01,效果如下图所示:
图-15
在文本框中输入任意的内容,点击提交,查看MyEclipse的控制台,输出结果如下图:
图-16
控制台输出的顺序可以证明代码的执行顺序为:实例化Action(实例化User并注入参数(调用set方法注入User对象(调用业务方法。
最终浏览器显示的效果如下图:
图-17
本案例的完整代码如下。
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; } }
其他代码没有变化,不再重复。
在Struts2框架下,如何将业务控制器Action的数据传递给JSP,并在JSP上显示出这些数据。
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表达式输出这些属性值。
实现此案例需要按照如下步骤进行。
步骤一:在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,在浏览器中刷新地址http://localhost:8088/StrutsDay01,效果如下图所示:
图-18
在文本框中输入任意的内容,点击提交,此时表单数据提交给了HelloAction,HelloAction接收到了表单数据后,跳转到了hello.jsp,我们在hello.jsp上使用了EL表达式来输出HelloAction的属性值,效果如下图:
图-19
小结
Action中追加属性时,不必区别何时加get方法,何时加set方法,通常每个属性都有get和set方法。
页面上写的EL表达式,实际上与二种注入方式中,对应的表单文本框的name表达式写法一致。
本案例的完整代码如下。
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>
其他代码没有变化,不再重复。
开发NetCTOSS项目中,资费模块的查询功能。要求查询全部的资费表数据并以列表的方式显示在页面上,其中分页功能先不实现。具体效果如下图:
图-20
查询功能只包含一个请求,即对资费数据的查询请求。在这次请求中,我们需要查询出资费所有的数据,然后将数据传递给查询列表页面,在页面上用表格显示出来。
使用Struts2处理这种请求的过程如下图:
图-21
在这个请求的过程中,我们需要编写的代码有:
这些代码的编写顺序,往往是按照他们的相互依赖关系来进行的,优先编写被依赖的代码,因此推荐代码编写的顺序是Entity(DAO(Action(struts.xml(JSP。由于步骤比较多,我们分2次来完成。本案例中我们先完成Entity、DAO代码的编写。
需要注意如下两点:
1、struts.xml写在JSP前面的原因是,开发完该配置文件,可以部署并启动项目,而在开发JSP时可以一边写一边刷新看效果,不必再重新启动服务了。
2、NetCTOSS项目我们采用ORACLE数据库开发。
实现此案例需要按照如下步骤进行。
步骤一:项目搭建
项目搭建包括项目的创建以及引入框架等操作,目前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; }
本案例的完整代码如下。
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; }
继续第5个案例,NetCTOSS资费列表。
本案例中,我们要完成Action、struts.xml、JSP代码的编写。
实现此案例需要按照如下步骤进行。
步骤一: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" "http://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" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</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)加拿大达内IT培训集团公司</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" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</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"> 遇到错误, <span id="secondes">5</span> 秒后将自动跳转,立即跳转请点击 <a href="javascript:resetTimer();">返回</a> </h1> </body> </html>
完成这一步后,我们先做一下阶段性测试,以验证是否能通过FindCostAction访问到find_cost.jsp。即部署NetCTOSS项目,然后启动tomcat,在浏览器地址栏中输入http://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="http://java.sun.com/jsp/jstl/core" prefix="c"%>
最后,我们使用JSTL标签和EL表达式来动态显示页面表格中的数据,代码如下:
#cold_bold<%@page pageEncoding="utf-8" isELIgnored="false"%> #cold_bold<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</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)加拿大达内IT培训集团公司</p> </div> </body> </html>
步骤四:单元测试
重新访问此功能,根据页面显示的数据测试代码是否正确。即在浏览器中刷新地址http://localhost:8088/NETCTOSS/cost/findCost,页面效果如下图:
图-26
本案例的完整代码如下。
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" "http://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="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</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)加拿大达内IT培训集团公司 </p> </div> </body> </html>
error.jsp完整代码:
<%@page pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</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"> 遇到错误, <span id="secondes">5</span> 秒后将自动跳转,立即跳转请点击 <a href="javascript:resetTimer();">返回</a> </h1> </body> </html>