1.5 JSP Model2开发模式应用样例
JSP Model2是JSP+JavaBean+Servlet的结合。这种开发模式完全遵循MVC设计模式,充分利用JSP和Servlet两种技术原有的优点,能更加明显地把显示和逻辑分离,使得代码比JSP Model1开发模式的容易管理,适用于大型项目的开发。
为了帮助获得JSP Model2开发模式开发Java EE的Web应用经验,本节介绍一个基于JSP+JavaBean+Servlet实现的例子:在线购物的B2C电子商务网站中用户注册登录应用。
1.5.1 电子商务网站说明
电子商务是运用现代通信技术、计算机和网络技术进行的一种社会经济形态,其目的是通过减低社会经营成本、提高社会生产效率、优化社会资源配置,从而实现社会财富的最大化利用。
电子商务按照经济活动的类别可分为两大类别。
B2B(Business to Business):企业间的电子商务,即企业与企业之间,通过网络进行产品或服务的经营活动。
B2C(Business to Customer):企业与消费者之间的电子商务,即企业通过网络为消费者提供一个产品或者服务的经营活动。
本小节要开发的案例就是一个微型的在线购物的B2C电子商务网站。主要包括以下几个模块:
● 用户注册、登录
● 商品浏览、搜索
● 购物车
● 结账、支付
● 订单处理
● 查看订单状态、详细信息
1.5.2 JSP Model2应用:在线购物的B2C电子商务网站—用户注册登录应用
我们将用JSP Model2开发模式实现在线购物的B2C电子商务网站—用户注册登录应用。几乎任何一个Web应用网站都提供了用户注册登录功能。
1.电子商务网站—用户注册登录应用需求
用户注册登录应用需求非常简单。对新用户提供注册功能、已注册用户提供登录功能。
按照JSP Model2模式,可以分析出用户注册登录应用中各组件之间关系如图1-38所示。
图1-38 用户注册登录应用组件关系
新用户注册流程:
(1)用户单击首页中的注册链接(newUser.jsp)。
(2)添加注册信息。
(3)使用JavaScript初步校验填写数据是否完整、合法,如果合法,继续下一步;如果不合法,转到(2)。
(4)控制器组件AccountServlet接收注册表单中的参数,封装注册参数到模型组件Account。创建模型组件AccountDAO对象,通过DAO对象中定义的业务接口把注册数据保存到数据库中。
(5)如果成功,把页面转发到userLogin.jsp;否则把页面转发到newUser.jsp,让用户重新添加信息,并且提示错误信息。
用户登录流程:
(1)进入登录页面。
(2)用户输入用户名和密码。
(3)使用JavaScript校验用户是否已经输入了用户名和密码,如果输入完整,继续下一步;否则,提示用户重新输入。
(4)控制器组件AccountServlet接收登录表单中的参数。通过模型组件AcccountDAO校验用户名和密码是否正确。
(5)如果校验正确,把用户登录信息保存在session,然后把视图转发到目标;否则,返回登录页面,要求重新登录。
2.分析数据
用户注册登录应用中所涉及到实体就只有系统账户,包含用户ID、用户密码、用户真实姓名、用户E-mail、用户状态、用户地址1、用户地址2、用户所在城市、用户所在省、邮编、用户所在国家或地区、联系电话等基本字段,系统账户实体对象设计如图1-39所示。
图1-39 系统账户实体对象
采用MS Sql Server 2005作为数据库服务器,构建电子商务网站系统数据库为topebusdb,创建系统账户数据表eb_account,SQL语句代码如下所示:
create table eb_account ( userid char(10) not null, password varchar(25) not null, email varchar(80) not null, chinaname varchar(80) not null, status char(2), address1 varchar(80) not null, address2 varchar(80), city varchar(80) not null, state varchar(80) not null, zip varchar(20) not null, country varchar(20) not null, phone varchar(80) not null, constraint pk_account primary key (userid) );
3.项目初始化
(1)在MyEclipse下构建Web Project项目,定义电子商务网站项目名称为TopEBus。
(2)在Web应用根目录下构建JS目录,编写常用的JavaScript文件存放其中。
(3)在Web应用根目录下建立images目录,导入相关图片。
(4)建立通用的页头(header)和页脚(footer)文件,放入Web应用根目录下的include文件目录中。可以通过<%@include>指令或者<jsp:include>动作把header和footer文件包含入其他页面中,详见【代码1-3】和【代码1-4】。
【代码1-3】 header.jsp文件。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" errorPage="/include/error.jsp" %> <html> <head><title>top_ebus电子商务网站</title> <meta content=”text/html; charset=gb2312" http-equiv=”Content-Type" /> </head><body> <table width=980 height=100 background="/images/duffersden001ba800.gif"> <tr height=80% width=100%> <td height=80% width=100% align=center><font size=+2 color=990099> TOP-EBusiness B2C电子商务网站</font></td> </tr> <tr height=20%><td align=right> <form action="#" method=post> <img src="/images/i_org.gif"border=0><a href="/index.jsp">商品类别 </a> <img src="/images/i_org.gif" border=0><a href="/order/viewOrders. do">我的订单 </a> <img src="/images/i_org.gif"border=0><a href="/cart/eb_c_cart.jsp"> 购物车 </a> <img src="/images/i_org.gif" border=0><a href="/account/userLogin. jsp">登录 </a> <img src="/images/i_org.gif" border=0><a href="/account/newUser. jsp">注册 </a> <img src="/images/i_org.gif" border=0><a href="/account/logout. jsp">注销</a> <INPUT id=keyword maxlength="10" name=keyword style="BORDER-RIGHT: #999999 1px solid; BORDER-TOP: #999999 1px solid; FONT-SIZE: 9pt; BORDER-LEFT:#999999 1px solid;WIDTH:100px;BORDER-BOTTOM:#999999 1px solid; BACKGROUND-COLOR: #f6f6f6"> < select id=searchType name=searchType> <option value="productid">产品ID</option> <option value="name">产品名称</option> <option value="category">产品类别</option> <option value="descn">产品描述</option> </select> <INPUT type=image height=21 alt="Go SEARCH" width=21 src="/images/ search.gif" align=absMiddle border=0> </form> </td></tr></table>
【代码1-4】 footer.jsp文件。
<table width="100%"cellspacing="0"cellpadding="0"border="0"bgcolor="#d6d6d6"> <tr><td valign="top" class="footer" height="20"> <center>@2011.四川托普信息技术职业学院.计算机系<br> <font size=2>成都市高新西区西区大道2000号</font></center> </td></tr> </table> </body> </html>
(5)建立通用的错误处理页面文件error.jsp。当JSP在运行出错时,就需要转向错误处理页面执行。在开发阶段,错误处理页面主要用于提供开发的错误调试信息,到产品实施阶段,错误处理页面应该进行替换,不能把原始的Java错误代码提供给用户。需要注意的是,错误处理页面的page指令中,必须包含isErrorPage="true"的声明。该文件同样放入include文件目录中。详细如【代码1-5】所示。
【代码1-5】 error.jsp文件。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="true" import="java.io.*"%> <HTML> <HEAD><META http-equiv="Content-Type" content="text/html; charset=UTF- 8"> <title>出错了!</title></HEAD> <BODY>发生了以下的错误:<br><hr><font color=red> <% String message = (String) request.getAttribute("message"); out.println("error message:<br>"); out.println(message+"<br>"); try { exception.printStackTrace(); StringWriter sout = new StringWriter(); PrintWriter pout = new PrintWriter(sout); exception.printStackTrace(pout); %> <pre><%=sout.toString()%> </pre> <%} catch (Exception e) {}%> </font> </BODY> </HTML>
(6)构建通用数据访问组件及字符转码工具组件。在src目录下建立包com.etop.topebus. common,在该包下创建数据库访问类EBusDb.java,该类主要用于封装获取数据库的连接的方法,执行各种SQL语句的方法,从而完成与数据库之间的交互;此外在该包下创建数据库属性文件ebusdb.properties,用于封装各类数据库连接的基础信息,包括数据库驱动、数据库URL、数据库用户名及密码等,在EBusDb文件可以读取其中信息从而构建数据库连接;在该包下建立字符转码工具组件类CharEncoding.java,该类提供将字符编码从ISO8859-1转换为UTF-8的方法接口。CharEncoding.java、ebusdb.properties、EBusDb.java文件详细如【代码1-6】~【代码1-8】所示。
【代码1-6】 CharEncoding.java文件。
public class CharEncoding { static public String toUTF(String input){ String output=""; try { output=new String(input.getBytes("iso8859-1"),"UTF-8"); } catch (UnsupportedEncodingException e) {e.printStackTrace(); } return output; } }
【代码1-7】 ebusdb.properties文件。
#MS SQL Server2005数据库连接信息 dirverclass=com.microsoft.sqlserver.jdbc.SQLServerDriver dburl=jdbc:sqlserver://localhost:1433;databaseName=topebusdb dbuser=sa dbpwd=mssqladmin #MySQL数据库连接信息 #dirverclass=com.mysql.jdbc.Driver #dburl=jdbc:mysql://127.0.0.1:3306/topebusdb #dbuser=root #dbpwd=mysqladmin
【代码1-8】 EBusDb.java文件。
public class EBusDb { private Connection conn=null; private Properties prop=null; InputStream ins=null; public Connection getCon(){//获取数据库连接对象接口 String driverclass,dburl,dbuser,dbpwd; prop=new Properties(); ins=getClass().getResourceAsStream("ebusdb.properties");// 读取属性文件 try { prop.load(ins); //加载属性文件 driverclass=prop.getProperty("dirverclass");//获取属性信息 dburl=prop.getProperty("dburl"); dbuser=prop.getProperty("dbuser"); dbpwd=prop.getProperty("dbpwd"); Class.forName(driverclass); //加载数据库驱动 conn=DriverManager.getConnection(dburl,dbuser,dbpwd); // 获取数据库连接对象 } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace();} return conn; } //关闭数据库操作对象接口 public void closedb(ResultSet rs,Statement stmt,PreparedStatement pstmt,Connection conn){ try { if(rs!=null) rs.close(); if(stmt!=null) stmt.close(); if(pstmt!=null)pstmt.close(); if(conn!=null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
4.构建Model组件
定义包com.etop.topebus.model.pojo,在该包下创建账户信息表对应JavaBean类Account.java,该类实现java.io.Serializable接口,用于在组件之间数据的传递。详细如【代码1-9】所示。
【代码1-9】 Account.java文件。
public class Account implements Serializable{ private String userid; //属性:对应数据表字段 private String password; …//略去部分属性定义 public Account() {}//无参构造函数 //对应属性的公开的getter和setter方法,略去 }
定义包com.etop.topebus.model.dao,在该包下创建账户处理组件类AccountDAO.java,该类用于实现对数据库的操作,包括用户登录校验以及用户注册等业务方法。详细如【代码1-10】所示。
【代码1-10】 AccountDAO.java文件。
public class AccountDAO { private Connection conn = null; private Statement stmt = null; private PreparedStatement pstmt = null; private ResultSet rs = null; public boolean loginValidate(String userid,String password){ //登录校验 boolean bl=false; conn = new EBusDb ().getCon(); // 获取数据库连接对象 String loginstr="select * from eb_account where userid=? and password=?";//构建sql语句 try { pstmt=conn.prepareStatement(loginstr); //建立预处理对象 pstmt.setString(1, userid); //给占位符赋值 pstmt.setString(2,password); rs=pstmt.executeQuery(); //执行查询 //迭代结果,是否存在符合条件记录,如果存在,设置标志为true if(rs.next()){ bl=true; } } catch (SQLException e) { e.printStackTrace(); } finally{ ebusdb.closedb(rs, stmt, pstmt, conn); } return bl; } public Account findByAccountid (String userid){ //根据用户ID获取用户信息 Account account=null; conn = new EBusDb ().getCon(); String findbyidstr="select * from eb_account where userid=?"; try { pstmt=conn.prepareStatement(findbyidstr); pstmt.setString(1, userid); rs=pstmt.executeQuery(); if(rs.next()){//如果有ID对应信息 account=new Account(); account.setUserid(rs.getString("userid")); account.setPassword(rs.getString("password")); //此处略去部分代码,将数据库表对应字段获取值封装在account对象中 } } catch (SQLException e) { e.printStackTrace(); } finally{ ebusdb.closedb(rs, stmt, pstmt, conn); } } return account; public void createNewAccount(Account account){ //增加新用户 conn = new EBusDb ().getCon(); String createnewastr="insert into account values(?,?,?,?,?,?,?,?,?,?, ?,?)"; try { pstmt=conn.prepareStatement(createnewastr); pstmt.setString(1, account.getUserid()); pstmt.setString(2,account.getPassword()); //此处略去部分代码,从account对象中获取属性值赋予占位符 pstmt.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally{ ebusdb.closedb(rs, stmt, pstmt, conn); } } }
5.构建Control组件
定义包com.etop.topebus.control,在该包下创建控制器类AccountServlet.java,用于执行用户注册及登录的流程控制,一方面获取用户注册及用户登录操作的表单数据,另一方面调用模型组件AccountDAO中对应业务方法进行处理,根据处理结果转发视图。详细如【代码1-11】所示。
【代码1-11】 AccountServlet.java文件。
public class AccountServlet extends HttpServlet { String defaultPage = "/index.jsp"; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void init(ServletConfig config) throws ServletException { super.init(config); try { defaultPage = config.getInitParameter("defaultPage"); if (defaultPage == null) defaultPage = "/index.jsp"; } catch (Exception e) {defaultPage = "/index.jsp";} } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); // 获取客户端请求操作类型参数,确定是登录操作还是注册操作 String operation = request.getParameter("op").trim(); AccountDAO adao = new AccountDAO();// 构建用户操作业务对象 // 判断客户端请求操作类型 if (operation != null && operation.equals("dologin")) {// 登录 String userid=request.getParameter("userid").trim();//获取登录参数 String password = request.getParameter("password").trim(); // 调用用户操作业务对象中登录校验接口 if (adao.loginValidate(userid, password)) {// 成功 Account loginaccount = adao.findByID(userid); //找到登录账户对象 HttpSession usersession = request.getSession(false); if (usersession != null) usersession.invalidate(); usersession = request.getSession(); usersession.setAttribute("loginaccount",loginaccount);//保存 在session中 } else {// 失败 defaultPage="/account/login.jsp"; request.setAttribute("errorMessage","请输入正确的用户名和密码!"); } } else {// 注册 String userid=request.getParameter("userid").trim();//获取注册账户ID Account registaccount=new Account();// 构建注册账户 registaccount.setUserid(userid); registaccount.setPassword(request.getParameter("password").trim()); //此处省略部分代码,将表单参数获取并封装在注册账户对象registaccount中 //判断是否已经存在该账户 if(adao.findByID(userid)!=null){//存在 defaultPage="/account/newUser.jsp"; request.setAttribute("nowaccount",registaccount); request.setAttribute("errorMessage","已经存在该账户名,请重 新注册!"); }else{//不存在 try {//调用用户操作业务对象创建新账户接口 adao.createNewAccount(registaccount); defaultPage="/account/userLogin.jsp"; } catch (RuntimeException e) { defaultPage="/account/newUser.jsp"; request.setAttribute("nowaccount",registaccount); request.setAttribute("errorMessage","在创建新用户时出 错!请重新再试!"); } } } getServletContext().getRequestDispatcher(defaultPage).forward(request ,response); //请求转发 } }
将该控制器组件在Web部署描述符web.xml文件中注册并加入映射,如【代码1-12】所示。
【代码1-12】 web.xml中控制器部署片段。
<servlet> <servlet-name>AccountServlet</servlet-name> <servlet-class>com.etop.topebus.control.AccountServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>AccountServlet</servlet-name> <url-pattern>/servlet/AccountServlet.do</url-pattern> </servlet-mapping>
6.构建View组件
新用户注册页面newUser.jsp,该页面采用Post方法提交表单数据,从而有助于数据的保密性。页面要求采用JavaScript对用户输入信息的完整性进行校验,如【代码1-13】所示。
【代码1-13】 newUser.jsp文件。
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.etop.topebus.model.pojo.Account" %> <html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <head><title>新用户注册</title> <jsp:include page="/include/header.jsp" flush="true" /> <script language="JavaScript"> function check(){ //略去输入校验逻辑 } </script> <% String errorMessage = (String) request.getAttribute("errorMessage"); Account nowaccount = (Account) request.getAttribute("nowaccount"); if(nowaccount==null) nowaccount=new Account(); %> <% if (errorMessage != null) { %> <br><span><font color=red> <%=errorMessage%></font></span><br> <%}%> <form method="post" action="../servlet/AccountServlet.do" name="form1"> <input type=hidden name="op" value="doreigst"> <TABLE class=font1 cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=750 align=center bgColor=#ffffff borderColorLight=#003686 border=1 align="center"> <tr><td colspan=2><IMG height=35 alt=""src="../images/new_user.gif" width=160 border=0><br></td></tr> <tr><td colspan="6"><IMG height=10 alt=""src="../images/spacer.gif" width=1 border=0></td></tr> <tr><td>用户名:</td><td><input size="15" name="userid" value="<%=nowaccount.getUserid() %>"></input></td></tr> <tr><td colspan="6"><IMG height=3 alt=""src="../images/spacer.gif" width=1 border=0></td></tr> <tr><td>密码:</td><td><input type="password" size="15" name="password"></input></td></tr> <tr><td>确认密码:</td><td><input type="password" size="15" name="confirmpassword"></input></td></tr> <tr><td>真实姓名:</td><td><input size="15" name="chinaname" value="<%=nowaccount.getChinaname() %>"></input></td></tr> <tr><td>电话:</td><td><input size="15" name="phone" value="<%=nowaccount.getPhone() %>"></input></td></tr> <tr><td>email:</td><td><input size="15" name="email" value="<%=nowaccount.getEmail() %>"></input></td></tr> <tr><td>地址1:</td><td><input size="15" name="address1" value="<%=nowaccount.getAddress1() %>"></input></td></tr> <tr><td>地址2:</td><td><input size="15" name="address2" value="<%=nowaccount.getAddress2() %>"></input></td></tr> <tr><td>城市:</td><td><input size="15" name="city" value="<%=nowaccount.getCity() %>"></input></td></tr> <tr><td>省:</td><td><input size="15" name="state" value="<%= nowaccount.getState() %>"></input></td></tr> <tr><td>邮编:</td><td><input size="15" name="zip" value="<%= nowaccount.getZip() %>"></input></td></tr> <tr><td>国别/地区:</td><td> <select name="country"> <option value="中国大陆">中国大陆</option> <option value="中国香港">中国香港</option> <option value="美国">美国</option> </select> </td> </tr> <tr><td colspan=2 align=center><input type="image" src="../ images/submit.gif" name="submit" onClick="return check()"></input></td></tr> </table> </form> <jsp:include page="/include/footer.jsp" flush="true" />
用户登录页面userLogin.jsp,该页面仍然需要采用JavaScript对用户输入信息的完整性进行校验,如【代码1-14】所示。
【代码1-14】 userLogin.jsp文件。
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%> <html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <head><title>::用户登录::</title> <script language="JavaScript"> //略去输入校验函数</script> <% String errorMessage=(String)request.getAttribute("errorMessage");%> <jsp:include page="/include/header.jsp" flush="true" /> <% if (errorMessage != null) {%> <center><font color=red> <%=errorMessage%></font></center><br> <%}%> <form action="../servlet/AccountServlet.do" method="POST"> <input type=hidden name="op" value="dologin"> <table align="center" border="0"> <tr><td colspan="2">请输入用户名和密码. <br /> </td></tr> <tr><td> 用 户 名 :</td><td><input type="text" name="userid" value="ebusaccount" /></td> </tr> <tr><td>密码:</td><td><input type="password" name="password" value="ebusaccount" /></td></tr> <tr><td> </td><td><input type="image" border="0" src="../images/button_submit.gif" /></td></tr> </table> </form> <jsp:include page="/include/footer.jsp" flush="true" />
7.部署应用
至此,已经使用JSP Model2开发模式完成在线购物的B2C电子商务网站中注册登录应用的实现,现在将项目TopEBus进行部署应用。由于部署应用相对较简单,本书略去此过程。