JavaWeb實(shí)現(xiàn)用戶登錄注冊(cè)功能實(shí)例代碼(基于Servlet+JSP+JavaBean模式)
下面通過(guò)通過(guò)圖文并茂的方式給大家介紹JavaWeb實(shí)現(xiàn)用戶登錄注冊(cè)功能實(shí)例代碼,一起看看吧。
一、Servlet+JSP+JavaBean開(kāi)發(fā)模式(MVC)介紹
Servlet+JSP+JavaBean模式(MVC)適合開(kāi)發(fā)復(fù)雜的web應(yīng)用,在這種模式下,servlet負(fù)責(zé)處理用戶請(qǐng)求,jsp負(fù)責(zé)數(shù)據(jù)顯示,javabean負(fù)責(zé)封裝數(shù)據(jù)。 Servlet+JSP+JavaBean模式程序各個(gè)模塊之間層次清晰,web開(kāi)發(fā)推薦采用此種模式。
這里以一個(gè)最常用的用戶登錄注冊(cè)程序來(lái)講解Servlet+JSP+JavaBean開(kāi)發(fā)模式,通過(guò)這個(gè)用戶登錄注冊(cè)程序綜合案例,把之前的學(xué)過(guò)的XML、Xpath、Servlet、jsp的知識(shí)點(diǎn)都串聯(lián)起來(lái)。
二、創(chuàng)建MVC架構(gòu)的Web項(xiàng)目
在MyEclipse中新創(chuàng)建一個(gè)webmvcframework項(xiàng)目,導(dǎo)入項(xiàng)目所需要的開(kāi)發(fā)包(jar包),創(chuàng)建項(xiàng)目所需要的包,在java開(kāi)發(fā)中,架構(gòu)的層次是以包的形式體現(xiàn)出來(lái)的
項(xiàng)目所需要的開(kāi)發(fā)包(jar包) | ||
序號(hào) | 開(kāi)發(fā)包名稱(chēng) | 描述 |
1 | dom4j-1.6.1.jar | dom4j用于操作XML文件 |
2 | jaxen-1.1-beta-6.jar | 用于解析XPath表達(dá)式 |
3 | commons-beanutils-1.8.0.jar | 工具類(lèi),用于處理bean對(duì)象 |
4 | commons-logging.jar | commons-beanutils-1.8.0.jar的依賴(lài)jar包 |
5 | jstl.jar | jstl標(biāo)簽庫(kù)和EL表達(dá)式依賴(lài)包 |
6 | standard.jar | jstl標(biāo)簽庫(kù)和EL表達(dá)式依賴(lài)包 |
一個(gè)良好的JavaWeb項(xiàng)目架構(gòu)應(yīng)該具有以上的11個(gè)包,這樣顯得層次分明,各個(gè)層之間的職責(zé)也很清晰明了,搭建JavaWeb項(xiàng)目架構(gòu)時(shí),就按照上面的1~11的序號(hào)順序創(chuàng)建包:domain→dao→dao.impl→service→service.impl→web.controller→web.UI→web.filter→web.listener→util→junit.test,包的層次創(chuàng)建好了,項(xiàng)目的架構(gòu)也就定下來(lái)了,當(dāng)然,在實(shí)際的項(xiàng)目開(kāi)發(fā)中,也不一定是完完全全按照
項(xiàng)目所需要的包 | |||
序號(hào) | 包名 | 描述 | 所屬層次 |
1 | me.gacl.domain | 存放系統(tǒng)的JavaBean類(lèi)(只包含簡(jiǎn)單的屬性以及屬性對(duì)應(yīng)的get和set方法,不包含具體的業(yè)務(wù)處理方法),提供給【數(shù)據(jù)訪問(wèn)層】、【業(yè)務(wù)處理層】、【W(wǎng)eb層】來(lái)使用 | domain(域模型)層 |
2 | me.gacl.dao | 存放訪問(wèn)數(shù)據(jù)庫(kù)的操作接口類(lèi) | 數(shù)據(jù)訪問(wèn)層 |
3 | me.gacl.dao.impl | 存放訪問(wèn)數(shù)據(jù)庫(kù)的操作接口的實(shí)現(xiàn)類(lèi) | |
4 | me.gacl.service | 存放處理系統(tǒng)業(yè)務(wù)接口類(lèi) | 業(yè)務(wù)處理層 |
5 | me.gacl.service.impl | 存放處理系統(tǒng)業(yè)務(wù)接口的實(shí)現(xiàn)類(lèi) | |
6 | me.gacl.web.controller | 存放作為系統(tǒng)控制器的Servlet | Web層(表現(xiàn)層) |
7 | me.gacl.web.UI | 存放為用戶提供用戶界面的servlet(UI指的是user interface) | |
8 | me.gacl.web.filter | 存放系統(tǒng)的用到的過(guò)濾器(Filter) | |
9 | me.gacl.web.listener | 存放系統(tǒng)的用到的監(jiān)聽(tīng)器(Listener) | |
10 | me.gacl.util | 存放系統(tǒng)的通用工具類(lèi),提供給【數(shù)據(jù)訪問(wèn)層】、【業(yè)務(wù)處理層】、【W(wǎng)eb層】來(lái)使用 | |
11 | junit.test | 存放系統(tǒng)的測(cè)試類(lèi) |
上面說(shuō)的來(lái)創(chuàng)建包的層次結(jié)構(gòu),而是根據(jù)項(xiàng)目的實(shí)際情況,可能還需要?jiǎng)?chuàng)建其
他的包,這個(gè)得根據(jù)項(xiàng)目的需要來(lái)定了
在src目錄(類(lèi)目錄)下面,創(chuàng)建用于保存用戶數(shù)據(jù)的xml文件(DB.xml)
在WEB-INF目錄下創(chuàng)建一個(gè)pages目錄,pages目錄存放系統(tǒng)的一些受保護(hù)(不允許用戶直接通過(guò)URL地址訪問(wèn))的jsp頁(yè)面,用戶要想訪問(wèn)這些受保護(hù)的jsp頁(yè)面,那么只能通過(guò)me.gacl.web.UI這個(gè)包里面的Servlet
創(chuàng)建好的項(xiàng)目如下圖(圖-1)所示:
圖-1
三、分層架構(gòu)的代碼編寫(xiě)
分層架構(gòu)的代碼也是按照【域模型層(domain)】→【數(shù)據(jù)訪問(wèn)層(dao、dao.impl)】→【業(yè)務(wù)處理層(service、service.impl)】→【表現(xiàn)層(web.controller、web.UI、web.filter、web.listener)】→【工具類(lèi)(util)】→【測(cè)試類(lèi)(junit.test)】的順序進(jìn)行編寫(xiě)的。
3.1、開(kāi)發(fā)domain層
在me.gacl.domain包下創(chuàng)建一個(gè)User類(lèi)
User類(lèi)具體代碼如下:
package me.gacl.domain; import java.io.Serializable; import java.util.Date; /** * @author gacl * 用戶實(shí)體類(lèi) */ public class User implements Serializable { private static final long serialVersionUID = -L; // 用戶ID private String id; // 用戶名 private String userName; // 用戶密碼 private String userPwd; // 用戶郵箱 private String email; // 用戶生日 private Date birthday; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPwd() { return userPwd; } public void setUserPwd(String userPwd) { this.userPwd = userPwd; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
3.2、開(kāi)發(fā)數(shù)據(jù)訪問(wèn)層(dao、dao.impl)
在me.gacl.dao包下創(chuàng)建一個(gè)IUserDao接口類(lèi),對(duì)于開(kāi)發(fā)接口類(lèi),我習(xí)慣以字母I作類(lèi)的前綴,這樣一眼就看出當(dāng)前這個(gè)類(lèi)是一個(gè)接口,這也算是一種良好的開(kāi)發(fā)習(xí)慣吧,通過(guò)看類(lèi)名就可以方便區(qū)分出是接口還是具體的實(shí)現(xiàn)類(lèi)。
IUserDao接口的具體代碼如下:
package me.gacl.dao; import me.gacl.domain.User; public interface IUserDao { /** * 根據(jù)用戶名和密碼來(lái)查找用戶 * @param userName * @param userPwd * @return 查到到的用戶 */ User find(String userName, String userPwd); /** * 添加用戶 * @param user */ void add(User user); /**根據(jù)用戶名來(lái)查找用戶 * @param userName * @return 查到到的用戶 */ User find(String userName); }
對(duì)于接口中的方法定義,這個(gè)只能是根據(jù)具體的業(yè)務(wù)來(lái)分析需要定義哪些方法了,但是無(wú)論是多么復(fù)雜的業(yè)務(wù),都離不開(kāi)基本的CRUD(增刪改查)操作,Dao層是直接和數(shù)據(jù)庫(kù)交互的,所以Dao層的接口一般都會(huì)有增刪改查這四種操作的相關(guān)方法。
在me.gacl.dao.impl包下創(chuàng)建一個(gè)UserDaoImpl類(lèi)
UserDaoImpl類(lèi)是IUserDao接口的具體實(shí)現(xiàn)類(lèi),對(duì)于接口的實(shí)現(xiàn)類(lèi)命名方式,我習(xí)慣以"接口名(去除前綴I)+impl"形式或者"接口名+impl"形式來(lái)命名:IUserDao(接口)→UserDaoImpl(實(shí)現(xiàn)類(lèi))或者IUserDao(接口)→IUserDaoImpl(實(shí)現(xiàn)類(lèi)),這也算是一些個(gè)人的編程習(xí)慣吧,平時(shí)看到的代碼大多數(shù)都是以這兩種形式中的一種來(lái)來(lái)命名接口的具體實(shí)現(xiàn)類(lèi)的,反正就是要能夠一眼看出接口對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)是哪一個(gè)就可以了。
UserDaoImpl類(lèi)的具體代碼如下:
package me.gacl.dao.impl; import java.text.SimpleDateFormat; import org.domj.Document; import org.domj.Element; import me.gacl.dao.IUserDao; import me.gacl.domain.User; import me.gacl.util.XmlUtils; /** * IUserDao接口的實(shí)現(xiàn)類(lèi) * @author gacl */ public class UserDaoImpl implements IUserDao { @Override public User find(String userName, String userPwd) { try{ Document document = XmlUtils.getDocument(); //使用XPath表達(dá)式來(lái)操作XML節(jié)點(diǎn) Element e = (Element) document.selectSingleNode("http://user[@userName='"+userName+"' and @userPwd='"+userPwd+"']"); if(e==null){ return null; } User user = new User(); user.setId(e.attributeValue("id")); user.setEmail(e.attributeValue("email")); user.setUserPwd(e.attributeValue("userPwd")); user.setUserName(e.attributeValue("userName")); String birth = e.attributeValue("birthday"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); user.setBirthday(sdf.parse(birth)); return user; }catch (Exception e) { throw new RuntimeException(e); } } @SuppressWarnings("deprecation") @Override public void add(User user) { try{ Document document = XmlUtils.getDocument(); Element root = document.getRootElement(); Element user_node = root.addElement("user"); //創(chuàng)建user結(jié)點(diǎn),并掛到root user_node.setAttributeValue("id", user.getId()); user_node.setAttributeValue("userName", user.getUserName()); user_node.setAttributeValue("userPwd", user.getUserPwd()); user_node.setAttributeValue("email", user.getEmail()); SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); user_node.setAttributeValue("birthday", sdf.format(user.getBirthday())); XmlUtils.writeXml(document); }catch (Exception e) { throw new RuntimeException(e); } } @Override public User find(String userName) { try{ Document document = XmlUtils.getDocument(); Element e = (Element) document.selectSingleNode("http://user[@userName='"+userName+"']"); if(e==null){ return null; } User user = new User(); user.setId(e.attributeValue("id")); user.setEmail(e.attributeValue("email")); user.setUserPwd(e.attributeValue("userPwd")); user.setUserName(e.attributeValue("userName")); String birth = e.attributeValue("birthday"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); user.setBirthday(sdf.parse(birth)); return user; }catch (Exception e) { throw new RuntimeException(e); } } }
3.3、開(kāi)發(fā)service層(service層對(duì)web層提供所有的業(yè)務(wù)服務(wù))
在me.gacl.service包中創(chuàng)建IUserService接口類(lèi)
IUserService接口的具體代碼如下:
package me.gacl.service; import me.gacl.domain.User; import me.gacl.exception.UserExistException; public interface IUserService { /** * 提供注冊(cè)服務(wù) * @param user * @throws UserExistException */ void registerUser(User user) throws UserExistException; /** * 提供登錄服務(wù) * @param userName * @param userPwd * @return */ User loginUser(String userName, String userPwd); }
在me.gacl.service.impl包中創(chuàng)建UserServiceImpl類(lèi)
UserServiceImpl類(lèi)為IUserService接口的具體實(shí)現(xiàn)類(lèi),具體代碼如下:
package me.gacl.service.impl; import me.gacl.dao.IUserDao; import me.gacl.dao.impl.UserDaoImpl; import me.gacl.domain.User; import me.gacl.exception.UserExistException; import me.gacl.service.IUserService; public class UserServiceImpl implements IUserService { private IUserDao userDao = new UserDaoImpl(); @Override public void registerUser(User user) throws UserExistException { if (userDao.find(user.getUserName())!=null) { //checked exception //unchecked exception //這里拋編譯時(shí)異常的原因:是我想上一層程序處理這個(gè)異常,以給用戶一個(gè)友好提示 throw new UserExistException("注冊(cè)的用戶名已存在?。?!"); } userDao.add(user); } @Override public User loginUser(String userName, String userPwd) { return userDao.find(userName, userPwd); } }
3.4、開(kāi)發(fā)web層
3.4.1、 開(kāi)發(fā)注冊(cè)功能
1、在me.gacl.web.UI包下寫(xiě)一個(gè)RegisterUIServlet為用戶提供注冊(cè)界面
RegisterUIServlet收到用戶請(qǐng)求后,就跳到register.jsp
RegisterUIServlet的代碼如下:
package me.gacl.web.UI; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author gacl * 為用戶提供注冊(cè)的用戶界面的Servlet * RegisterUIServlet負(fù)責(zé)為用戶輸出注冊(cè)界面 * 當(dāng)用戶訪問(wèn)RegisterUIServlet時(shí),就跳轉(zhuǎn)到WEB-INF/pages目錄下的register.jsp頁(yè)面 */ public class RegisterUIServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
2、在/WEB-INF/pages/目錄下編寫(xiě)用戶注冊(cè)的jsp頁(yè)面register.jsp
凡是位于WEB-INF目錄下的jsp頁(yè)面是無(wú)法直接通過(guò)URL地址直接訪問(wèn)的,
在開(kāi)發(fā)中如果項(xiàng)目中有一些敏感web資源不想被外界直接訪問(wèn),那么可以考慮將這些敏感的web資源放到WEB-INF目錄下,這樣就可以禁止外界直接通過(guò)URL來(lái)訪問(wèn)了。
register.jsp頁(yè)面的代碼如下:
<%@ page language="java" pageEncoding="UTF-"%> <!DOCTYPE HTML> <html> <head> <title>用戶注冊(cè)</title> </head> <body style="text-align: center;"> <form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post"> <table width="%" border=""> <tr> <td>用戶名</td> <td> <input type="text" name="userName"> </td> </tr> <tr> <td>密碼</td> <td> <input type="password" name="userPwd"> </td> </tr> <tr> <td>確認(rèn)密碼</td> <td> <input type="password" name="confirmPwd"> </td> </tr> <tr> <td>郵箱</td> <td> <input type="text" name="email"> </td> </tr> <tr> <td>生日</td> <td> <input type="text" name="birthday"> </td> </tr> <tr> <td> <input type="reset" value="清空"> </td> <td> <input type="submit" value="注冊(cè)"> </td> </tr> </table> </form> </body> </html>
register.jsp中的<form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post">指明表單提交后,交給RegisterServlet進(jìn)行處理
3、在me.gacl.web.controller包下編寫(xiě)用于處理用戶注冊(cè)的RegisterServlet
RegisterServlet擔(dān)任著以下幾個(gè)職責(zé):
1、接收客戶端提交到服務(wù)端的表單數(shù)據(jù)。
2、校驗(yàn)表單數(shù)據(jù)的合法性,如果校驗(yàn)失敗跳回到register.jsp,并回顯錯(cuò)誤信息。
3、如果校驗(yàn)通過(guò),調(diào)用service層向數(shù)據(jù)庫(kù)中注冊(cè)用戶。
為了方便RegisterServlet接收表單數(shù)據(jù)和校驗(yàn)表單數(shù)據(jù),在此我設(shè)計(jì)一個(gè)用于校驗(yàn)注冊(cè)表單數(shù)據(jù)RegisterFormbean,再寫(xiě)WebUtils工具類(lèi),封裝客戶端提交的表單數(shù)據(jù)到formbean中。
在me.gacl.web.formbean包下創(chuàng)建一個(gè)用于校驗(yàn)注冊(cè)表單數(shù)據(jù)RegisterFormbean
RegisterFormbean代碼如下:
package me.gacl.web.formbean; import java.util.HashMap; import java.util.Map; import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; /** * 封裝的用戶注冊(cè)表單bean,用來(lái)接收register.jsp中的表單輸入項(xiàng)的值 * RegisterFormBean中的屬性與register.jsp中的表單輸入項(xiàng)的name一一對(duì)應(yīng) * RegisterFormBean的職責(zé)除了負(fù)責(zé)接收register.jsp中的表單輸入項(xiàng)的值之外還擔(dān)任著校驗(yàn)表單輸入項(xiàng)的值的合法性 * @author gacl * */ public class RegisterFormBean { //RegisterFormBean中的屬性與register.jsp中的表單輸入項(xiàng)的name一一對(duì)應(yīng) //<input type="text" name="userName"/> private String userName; //<input type="password" name="userPwd"/> private String userPwd; //<input type="password" name="confirmPwd"/> private String confirmPwd; //<input type="text" name="email"/> private String email; //<input type="text" name="birthday"/> private String birthday; /** * 存儲(chǔ)校驗(yàn)不通過(guò)時(shí)給用戶的錯(cuò)誤提示信息 */ private Map<String, String> errors = new HashMap<String, String>(); public Map<String, String> getErrors() { return errors; } public void setErrors(Map<String, String> errors) { this.errors = errors; } /* * validate方法負(fù)責(zé)校驗(yàn)表單輸入項(xiàng) * 表單輸入項(xiàng)校驗(yàn)規(guī)則: * private String userName; 用戶名不能為空,并且要是-的字母 abcdABcd * private String userPwd; 密碼不能為空,并且要是-的數(shù)字 * private String confirmPwd; 兩次密碼要一致 * private String email; 可以為空,不為空要是一個(gè)合法的郵箱 * private String birthday; 可以為空,不為空時(shí),要是一個(gè)合法的日期 */ public boolean validate() { boolean isOk = true; if (this.userName == null || this.userName.trim().equals("")) { isOk = false; errors.put("userName", "用戶名不能為空?。?); } else { if (!this.userName.matches("[a-zA-Z]{,}")) { isOk = false; errors.put("userName", "用戶名必須是-位的字母?。?); } } if (this.userPwd == null || this.userPwd.trim().equals("")) { isOk = false; errors.put("userPwd", "密碼不能為空?。?); } else { if (!this.userPwd.matches("\\d{,}")) { isOk = false; errors.put("userPwd", "密碼必須是-位的數(shù)字?。?); } } // private String password; 兩次密碼要一致 if (this.confirmPwd != null) { if (!this.confirmPwd.equals(this.userPwd)) { isOk = false; errors.put("confirmPwd", "兩次密碼不一致?。?); } } // private String email; 可以為空,不為空要是一個(gè)合法的郵箱 if (this.email != null && !this.email.trim().equals("")) { if (!this.email.matches("\\w+@\\w+(\\.\\w+)+")) { isOk = false; errors.put("email", "郵箱不是一個(gè)合法郵箱??!"); } } // private String birthday; 可以為空,不為空時(shí),要是一個(gè)合法的日期 if (this.birthday != null && !this.birthday.trim().equals("")) { try { DateLocaleConverter conver = new DateLocaleConverter(); conver.convert(this.birthday); } catch (Exception e) { isOk = false; errors.put("birthday", "生日必須要是一個(gè)日期?。?); } } return isOk; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPwd() { return userPwd; } public void setUserPwd(String userPwd) { this.userPwd = userPwd; } public String getConfirmPwd() { return confirmPwd; } public void setConfirmPwd(String confirmPwd) { this.confirmPwd = confirmPwd; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getBirthday() { return birthday; } public void setBirthday(String birthday) { this.birthday = birthday; } }
在me.gacl.util包下創(chuàng)建一個(gè)WebUtils工具類(lèi),該工具類(lèi)的功能就是封裝客戶端提交的表單數(shù)據(jù)到formbean中
package me.gacl.util; import java.util.Enumeration; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import org.apache.commons.beanutils.BeanUtils; /** * @author gacl * 把request對(duì)象中的請(qǐng)求參數(shù)封裝到bean中 */ public class WebUtils { /** * 將request對(duì)象轉(zhuǎn)換成T對(duì)象 * @param request * @param clazz * @return */ public static <T> T requestBean(HttpServletRequest request,Class<T> clazz){ try{ T bean = clazz.newInstance(); Enumeration<String> e = request.getParameterNames(); while(e.hasMoreElements()){ String name = (String) e.nextElement(); String value = request.getParameter(name); BeanUtils.setProperty(bean, name, value); } return bean; }catch (Exception e) { throw new RuntimeException(e); } } /** * 生成UUID * @return */ public static String makeId(){ return UUID.randomUUID().toString(); } }
最后看一下負(fù)責(zé)處理用戶注冊(cè)的RegisterServlet完整代碼:
package me.gacl.web.controller; import java.io.IOException; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; import me.gacl.domain.User; import me.gacl.exception.UserExistException; import me.gacl.service.IUserService; import me.gacl.service.impl.UserServiceImpl; import me.gacl.util.WebUtils; import me.gacl.web.formbean.RegisterFormBean; /** * 處理用戶注冊(cè)的Servlet * @author gacl * */ public class RegisterServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //將客戶端提交的表單數(shù)據(jù)封裝到RegisterFormBean對(duì)象中 RegisterFormBean formbean = WebUtils.requestBean(request,RegisterFormBean.class); //校驗(yàn)用戶注冊(cè)填寫(xiě)的表單數(shù)據(jù) if (formbean.validate() == false) {//如果校驗(yàn)失敗 //將封裝了用戶填寫(xiě)的表單數(shù)據(jù)的formbean對(duì)象發(fā)送回register.jsp頁(yè)面的form表單中進(jìn)行顯示 request.setAttribute("formbean", formbean); //校驗(yàn)失敗就說(shuō)明是用戶填寫(xiě)的表單數(shù)據(jù)有問(wèn)題,那么就跳轉(zhuǎn)回register.jsp request.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(request, response); return; } User user = new User(); try { // 注冊(cè)字符串到日期的轉(zhuǎn)換器 ConvertUtils.register(new DateLocaleConverter(), Date.class); BeanUtils.copyProperties(user, formbean);//把表單的數(shù)據(jù)填充到j(luò)avabean中 user.setId(WebUtils.makeId());//設(shè)置用戶的Id屬性 IUserService service = new UserServiceImpl(); //調(diào)用service層提供的注冊(cè)用戶服務(wù)實(shí)現(xiàn)用戶注冊(cè) service.registerUser(user); String message = String.format( "注冊(cè)成功?。∶牒鬄槟詣?dòng)跳到登錄頁(yè)面??!<meta http-equiv='refresh' content=';url=%s'/>", request.getContextPath()+"/servlet/LoginUIServlet"); request.setAttribute("message",message); request.getRequestDispatcher("/message.jsp").forward(request,response); } catch (UserExistException e) { formbean.getErrors().put("userName", "注冊(cè)用戶已存在?。?); request.setAttribute("formbean", formbean); request.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); // 在后臺(tái)記錄異常 request.setAttribute("message", "對(duì)不起,注冊(cè)失敗??!"); request.getRequestDispatcher("/message.jsp").forward(request,response); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
用戶注冊(cè)時(shí)如果填寫(xiě)的表單數(shù)據(jù)校驗(yàn)不通過(guò),那么服務(wù)器端就將一個(gè)存儲(chǔ)了錯(cuò)誤提示消息和表單數(shù)據(jù)的formbean對(duì)象存儲(chǔ)到request對(duì)象中,然后發(fā)送回register.jsp頁(yè)面,因此我們需要在register.jsp頁(yè)面中取出request對(duì)象中formbean對(duì)象,然后將用戶填寫(xiě)的表單數(shù)據(jù)重新回顯到對(duì)應(yīng)的表單項(xiàng)上面,將出錯(cuò)時(shí)的提示消息也顯示到form表單上面,讓用戶知道是哪些數(shù)據(jù)填寫(xiě)不合法!
修改register.jsp頁(yè)面,代碼如下:
<%@ page language="java" pageEncoding="UTF-"%> <!DOCTYPE HTML> <html> <head> <title>用戶注冊(cè)</title> </head> <body style="text-align: center;"> <form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post"> <table width="%" border=""> <tr> <td>用戶名</td> <td> <%--使用EL表達(dá)式${}提取存儲(chǔ)在request對(duì)象中的formbean對(duì)象中封裝的表單數(shù)據(jù)(formbean.userName)以及錯(cuò)誤提示消息(formbean.errors.userName)--%> <input type="text" name="userName" value="${formbean.userName}">${formbean.errors.userName} </td> </tr> <tr> <td>密碼</td> <td> <input type="password" name="userPwd" value="${formbean.userPwd}">${formbean.errors.userPwd} </td> </tr> <tr> <td>確認(rèn)密碼</td> <td> <input type="password" name="confirmPwd" value="${formbean.confirmPwd}">${formbean.errors.confirmPwd} </td> </tr> <tr> <td>郵箱</td> <td> <input type="text" name="email" value="${formbean.email}">${formbean.errors.email} </td> </tr> <tr> <td>生日</td> <td> <input type="text" name="birthday" value="${formbean.birthday}">${formbean.errors.birthday} </td> </tr> <tr> <td> <input type="reset" value="清空"> </td> <td> <input type="submit" value="注冊(cè)"> </td> </tr> </table> </form> </body> </html>
到此,用戶注冊(cè)功能就算是開(kāi)發(fā)完成了!
下面測(cè)試一下開(kāi)發(fā)好的用戶注冊(cè)功能:
輸入U(xiǎn)RL地址:http://localhost:8080/webmvcframework/servlet/RegisterUIServlet訪問(wèn)register.jsp頁(yè)面,運(yùn)行效果如下:
如果輸入的表單項(xiàng)不符合校驗(yàn)規(guī)則,那么是無(wú)法進(jìn)行注冊(cè)的,運(yùn)行效果如下:
3.4.2、 開(kāi)發(fā)登錄功能
1、在me.gacl.web.UI包下寫(xiě)一個(gè)LoginUIServlet為用戶提供登錄界面
LoginUIServlet收到用戶請(qǐng)求后,就跳到login.jsp
LoginUIServlet的代碼如下:
package me.gacl.web.UI; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author gacl * LoginUIServlet負(fù)責(zé)為用戶輸出登陸界面 * 當(dāng)用戶訪問(wèn)LoginUIServlet時(shí),就跳轉(zhuǎn)到WEB-INF/pages目錄下的login.jsp頁(yè)面 */ public class LoginUIServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("/WEB-INF/pages/login.jsp").forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
2、在/WEB-INF/pages/目錄下編寫(xiě)用戶登錄的jsp頁(yè)面login.jsp
login.jsp頁(yè)面的代碼如下:
<%@ page language="java" pageEncoding="UTF-"%> <!DOCTYPE HTML> <html> <head> <title>用戶登陸</title> </head> <body> <form action="${pageContext.request.contextPath }/servlet/LoginServlet" method="post"> 用戶名:<input type="text" name="username"><br/> 密碼:<input type="password" name="password"><br/> <input type="submit" value="登陸"> </form> </body> </html>
login.jsp中的<form action="${pageContext.request.contextPath}/servlet/LoginServlet" method="post">指明表單提交后,交給LoginServlet進(jìn)行處理。
3、在me.gacl.web.controller包下編寫(xiě)用于處理用戶登錄的LoginServlet
LoginServlet的代碼如下:
package me.gacl.web.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import me.gacl.domain.User; import me.gacl.service.IUserService; import me.gacl.service.impl.UserServiceImpl; /** * 處理用戶登錄的servlet * @author gacl * */ public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //獲取用戶填寫(xiě)的登錄用戶名 String username = request.getParameter("username"); //獲取用戶填寫(xiě)的登錄密碼 String password = request.getParameter("password"); IUserService service = new UserServiceImpl(); //用戶登錄 User user = service.loginUser(username, password); if(user==null){ String message = String.format( "對(duì)不起,用戶名或密碼有誤??!請(qǐng)重新登錄!秒后為您自動(dòng)跳到登錄頁(yè)面?。?lt;meta http-equiv='refresh' content=';url=%s'", request.getContextPath()+"/servlet/LoginUIServlet"); request.setAttribute("message",message); request.getRequestDispatcher("/message.jsp").forward(request, response); return; } //登錄成功后,就將用戶存儲(chǔ)到session中 request.getSession().setAttribute("user", user); String message = String.format( "恭喜:%s,登陸成功!本頁(yè)將在秒后跳到首頁(yè)??!<meta http-equiv='refresh' content=';url=%s'", user.getUserName(), request.getContextPath()+"/index.jsp"); request.setAttribute("message",message); request.getRequestDispatcher("/message.jsp").forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
到此,用戶登錄的功能就算是開(kāi)發(fā)完成了。
下面測(cè)試一下開(kāi)發(fā)好的用戶登錄功能,輸入U(xiǎn)RL地址:http://localhost:8080/webmvcframework/servlet/LoginUIServlet訪問(wèn)login.jsp頁(yè)面,輸入正確的用戶名和密碼進(jìn)行登錄,運(yùn)行效果如下:
如果輸入的用戶名和密碼錯(cuò)誤,那么就無(wú)法登錄成功,運(yùn)行效果如下:
3.4.3、 開(kāi)發(fā)注銷(xiāo)功能
在me.gacl.web.controller包下編寫(xiě)用于處理用戶注銷(xiāo)的LogoutServlet
LogoutServlet的代碼如下:
package me.gacl.web.controller; import java.io.IOException; import java.text.MessageFormat; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class LogoutServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //移除存儲(chǔ)在session中的user對(duì)象,實(shí)現(xiàn)注銷(xiāo)功能 request.getSession().removeAttribute("user"); //由于字符串中包含有單引號(hào),在這種情況下使用MessageFormat.format方法拼接字符串時(shí)就會(huì)有問(wèn)題 //MessageFormat.format方法只是把字符串中的單引號(hào)去掉,不會(huì)將內(nèi)容填充到指定的占位符中 String tempStr = MessageFormat.format( "注銷(xiāo)成功?。∶牒鬄槟詣?dòng)跳到登錄頁(yè)面??!<meta http-equiv='refresh' content=';url={}'/>", request.getContextPath()+"/servlet/LoginUIServlet"); System.out.println(tempStr);//輸出結(jié)果:注銷(xiāo)成功!!秒后為您自動(dòng)跳到登錄頁(yè)面?。?lt;meta http-equiv=refresh content=;url={}/> System.out.println("---------------------------------------------------------"); /** * 要想解決"如果要拼接的字符串包含有單引號(hào),那么MessageFormat.format方法就只是把字符串中的單引號(hào)去掉,不會(huì)將內(nèi)容填充到指定的占位符中"這個(gè)問(wèn)題, * 那么可以需要使用單引號(hào)引起來(lái)的字符串中使用個(gè)單引號(hào)引起來(lái),例如:"<meta http-equiv=''refresh'' content='';url={}''/>" * 這樣MessageFormat.format("<meta http-equiv=''refresh'' content='';url={}''/>","index.jsp")就可以正常返回 * <meta http-equiv=''refresh'' content='';url=index.jsp'/> */ String tempStr = MessageFormat.format( "注銷(xiāo)成功?。∶牒鬄槟詣?dòng)跳到登錄頁(yè)面??!<meta http-equiv=''refresh'' content='';url={}''/>", request.getContextPath()+"/servlet/LoginUIServlet"); /** * 輸出結(jié)果: * 注銷(xiāo)成功??!秒后為您自動(dòng)跳到登錄頁(yè)面??! * <meta http-equiv='refresh' content=';url=/webmvcframework/servlet/LoginUIServlet'/> */ System.out.println(tempStr); String message = String.format( "注銷(xiāo)成功!!秒后為您自動(dòng)跳到登錄頁(yè)面??!<meta http-equiv='refresh' content=';url=%s'/>", request.getContextPath()+"/servlet/LoginUIServlet"); request.setAttribute("message",message); request.getRequestDispatcher("/message.jsp").forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
用戶登錄成功后,會(huì)將登錄的用戶信息存儲(chǔ)在session中,所以我們要將存儲(chǔ)在session中的user刪除掉,這樣就可以實(shí)現(xiàn)用戶注銷(xiāo)了。
用戶登錄成功后就會(huì)跳轉(zhuǎn)到index.jsp頁(yè)面,在index.jsp頁(yè)面中放一個(gè)【退出登陸】按鈕,當(dāng)點(diǎn)擊【退出登陸】按鈕時(shí),就訪問(wèn)LogoutServlet,將用戶注銷(xiāo)。
index.jsp的代碼如下:
<%@ page language="java" pageEncoding="UTF-"%> <%--為了避免在jsp頁(yè)面中出現(xiàn)java代碼,這里引入jstl標(biāo)簽庫(kù),利用jstl標(biāo)簽庫(kù)提供的標(biāo)簽來(lái)做一些邏輯判斷處理 --%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE HTML> <html> <head> <title>首頁(yè)</title> <script type="text/javascript"> function doLogout(){ //訪問(wèn)LogoutServlet注銷(xiāo)當(dāng)前登錄的用戶 window.location.href="${pageContext.request.contextPath}/servlet/LogoutServlet"; } </script> </head> <body> <h>孤傲蒼狼的網(wǎng)站</h> <hr/> <c:if test="${user==null}"> <a href="${pageContext.request.contextPath}/servlet/RegisterUIServlet" target="_blank">注冊(cè)</a> <a href="${pageContext.request.contextPath}/servlet/LoginUIServlet">登陸</a> </c:if> <c:if test="${user!=null}"> 歡迎您:${user.userName} <input type="button" value="退出登陸" onclick="doLogout()"> </c:if> <hr/> </body> </html>
測(cè)試開(kāi)發(fā)好的注銷(xiāo)功能,效果如下:
到此,所有的功能都開(kāi)發(fā)完成了,測(cè)試也通過(guò)了。
四、開(kāi)發(fā)總結(jié)
通過(guò)這個(gè)小例子,可以了解到mvc分層架構(gòu)的項(xiàng)目搭建,在平時(shí)的項(xiàng)目開(kāi)發(fā)中,也都是按照如下的順序來(lái)進(jìn)行開(kāi)發(fā)的:
1、搭建開(kāi)發(fā)環(huán)境
1.1 創(chuàng)建web項(xiàng)目
1.2 導(dǎo)入項(xiàng)目所需的開(kāi)發(fā)包
1.3 創(chuàng)建程序的包名,在java中是以包來(lái)體現(xiàn)項(xiàng)目的分層架構(gòu)的
2、開(kāi)發(fā)domain
把一張要操作的表當(dāng)成一個(gè)VO類(lèi)(VO類(lèi)只定義屬性以及屬性對(duì)應(yīng)的get和set方法,沒(méi)有涉及到具體業(yè)務(wù)的操作方法),VO表示的是值對(duì)象,通俗地說(shuō),就是把表中的每一條記錄當(dāng)成一個(gè)對(duì)象,表中的每一個(gè)字段就作為這個(gè)對(duì)象的屬性。每往表中插入一條記錄,就相當(dāng)于是把一個(gè)VO類(lèi)的實(shí)例對(duì)象插入到數(shù)據(jù)表中,對(duì)數(shù)據(jù)表進(jìn)行操作時(shí),都是直接把一個(gè)VO類(lèi)的對(duì)象寫(xiě)入到表中,一個(gè)VO類(lèi)對(duì)象就是一條記錄。每一個(gè)VO對(duì)象可以表示一張表中的一行記錄,VO類(lèi)的名稱(chēng)要和表的名稱(chēng)一致或者對(duì)應(yīng)。
3、開(kāi)發(fā)dao
3.1 DAO操作接口:每一個(gè)DAO操作接口規(guī)定了,一張表在一個(gè)項(xiàng)目中的具體操作方法,此接口的名稱(chēng)最好按照如下格式編寫(xiě):“I表名稱(chēng)Dao”。
├DAO接口里面的所有方法按照以下的命名編寫(xiě):
├更新數(shù)據(jù)庫(kù):doXxx()
├查詢(xún)數(shù)據(jù)庫(kù):findXxx()或getXxx()
3.2 DAO操作接口的實(shí)現(xiàn)類(lèi):實(shí)現(xiàn)類(lèi)中完成具體的增刪改查操作
├此實(shí)現(xiàn)類(lèi)完成的只是數(shù)據(jù)庫(kù)中最核心的操作,并沒(méi)有專(zhuān)門(mén)處理數(shù)據(jù)庫(kù)的打開(kāi)和關(guān)閉,因?yàn)檫@些操作與具體的業(yè)務(wù)操作無(wú)關(guān)。
4、開(kāi)發(fā)service(service 對(duì)web層提供所有的業(yè)務(wù)服務(wù))
5、開(kāi)發(fā)web層
以上內(nèi)容是小編給大家介紹的JavaWeb實(shí)現(xiàn)用戶登錄注冊(cè)功能實(shí)例代碼(基于Servlet+JSP+JavaBean模式),希望對(duì)大家有所幫助!
- js實(shí)現(xiàn)登錄與注冊(cè)界面
- JSP實(shí)現(xiàn)用戶登錄、注冊(cè)和退出功能
- js實(shí)現(xiàn)登錄注冊(cè)框手機(jī)號(hào)和驗(yàn)證碼校驗(yàn)(前端部分)
- 原生js驗(yàn)證簡(jiǎn)潔注冊(cè)登錄頁(yè)面
- JSP+MySQL實(shí)現(xiàn)網(wǎng)站的登錄與注冊(cè)小案例
- 使用JSP實(shí)現(xiàn)簡(jiǎn)單的用戶登錄注冊(cè)頁(yè)面示例代碼解析
- Servlet+JavaBean+JSP打造Java Web注冊(cè)與登錄功能
- node.js實(shí)現(xiàn)登錄注冊(cè)頁(yè)面
- vue.js實(shí)現(xiàn)用戶評(píng)論、登錄、注冊(cè)、及修改信息功能
- js實(shí)現(xiàn)本地持久化存儲(chǔ)登錄注冊(cè)
相關(guān)文章
spring boot整合redis實(shí)現(xiàn)shiro的分布式session共享的方法
本篇文章主要介紹了spring boot整合redis實(shí)現(xiàn)shiro的分布式session共享的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01java版微信公眾平臺(tái)消息接口應(yīng)用示例
這篇文章主要介紹了java版微信公眾平臺(tái)消息接口應(yīng)用,結(jié)合實(shí)例形式對(duì)比分析了PHP與java應(yīng)用微信公眾平臺(tái)接口的相關(guān)調(diào)用與操作技巧,需要的朋友可以參考下2017-07-07java中response對(duì)象用法實(shí)例分析
這篇文章主要介紹了java中response對(duì)象用法,結(jié)合實(shí)例形式分析了Java中response對(duì)象的功能及具體使用技巧,需要的朋友可以參考下2015-12-12Java實(shí)現(xiàn)局域網(wǎng)聊天小程序
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)局域網(wǎng)聊天小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05Spark學(xué)習(xí)筆記之Spark中的RDD的具體使用
這篇文章主要介紹了Spark學(xué)習(xí)筆記之Spark中的RDD的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06深入了解Java核心類(lèi)庫(kù)--BigDecimal和System類(lèi)
這篇文章主要為大家詳細(xì)介紹了javaBigDecimal和System類(lèi)定義與使用的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能給你帶來(lái)幫助2021-07-07