一、Servlet+JSP+JavaBean開發模式(MVC)介紹
Servlet+JSP+JavaBean模式(MVC)適合開發復雜的web應用,在這種模式下,servlet負責處理用戶請求,jsp負責數據顯示,javabean負責封裝數據。 Servlet+JSP+JavaBean模式程序各個模塊之間層次清晰,web開發推薦采用此種模式。
這里以一個最常用的用戶登錄注冊程序來講解Servlet+JSP+JavaBean開發模式,通過這個用戶登錄注冊程序綜合案例,把之前的學過的XML、Xpath、Servlet、jsp的知識點都串聯起來。
二、創建MVC架構的Web項目
在MyEclipse中新創建一個webmvcframework項目,導入項目所需要的開發包(jar包),創建項目所需要的包,在java開發中,架構的層次是以包的形式體現出來的
項目所需要的開發包(jar包) | ||
序號 | 開發包名稱 | 描述 |
1 | dom4j-1.6.1.jar | dom4j用於操作XML文件 |
2 | jaxen-1.1-beta-6.jar | 用於解析XPath表達式 |
3 | commons-beanutils-1.8.0.jar | 工具類,用於處理bean對象 |
4 | commons-logging.jar | commons-beanutils-1.8.0.jar的依賴jar包 |
5 | jstl.jar | jstl標簽庫和EL表達式依賴包 |
6 | standard.jar | jstl標簽庫和EL表達式依賴包 |
項目所需要的包 | |||
序號 | 包名 | 描述 | 所屬層次 |
1 | me.gacl.domain | 存放系統的JavaBean類(只包含簡單的屬性以及屬性對應的get和set方法,不包含具體的業務處理方法),提供給【數據訪問層】、【業務處理層】、【Web層】來使用 | domain(域模型)層 |
2 | me.gacl.dao | 存放訪問數據庫的操作接口類 | 數據訪問層 |
3 | me.gacl.dao.impl | 存放訪問數據庫的操作接口的實現類 | |
4 | me.gacl.service | 存放處理系統業務接口類 | 業務處理層 |
5 | me.gacl.service.impl | 存放處理系統業務接口的實現類 | |
6 | me.gacl.web.controller | 存放作為系統控制器的Servlet | Web層(表現層) |
7 | me.gacl.web.UI | 存放為用戶提供用戶界面的servlet(UI指的是user interface) | |
8 | me.gacl.web.filter | 存放系統的用到的過濾器(Filter) | |
9 | me.gacl.web.listener | 存放系統的用到的監聽器(Listener) | |
10 | me.gacl.util | 存放系統的通用工具類,提供給【數據訪問層】、【業務處理層】、【Web層】來使用 | |
11 | junit.test | 存放系統的測試類 |
一個良好的JavaWeb項目架構應該具有以上的11個包,這樣顯得層次分明,各個層之間的職責也很清晰明了,搭建JavaWeb項目架構時,就按照上面的1~11的序號順序創建包:domain→dao→dao.impl→service→service.impl→web.controller→web.UI→web.filter→web.listener→util→junit.test,包的層次創建好了,項目的架構也就定下來了,當然,在實際的項目開發中,也不一定是完完全全按照上面說的來創建包的層次結構,而是根據項目的實際情況,可能還需要創建其他的包,這個得根據項目的需要來定了
在src目錄(類目錄)下面,創建用於保存用戶數據的xml文件(DB.xml)
在WEB-INF目錄下創建一個pages目錄,pages目錄存放系統的一些受保護(不允許用戶直接通過URL地址訪問)的jsp頁面,用戶要想訪問這些受保護的jsp頁面,那么只能通過me.gacl.web.UI這個包里面的Servlet
創建好的項目如下圖(圖-1)所示:
圖-1
三、分層架構的代碼編寫
分層架構的代碼也是按照【域模型層(domain)】→【數據訪問層(dao、dao.impl)】→【業務處理層(service、service.impl)】→【表現層(web.controller、web.UI、web.filter、web.listener)】→【工具類(util)】→【測試類(junit.test)】的順序進行編寫的。
3.1、開發domain層
在me.gacl.domain包下創建一個User類
User類具體代碼如下:
1 package me.gacl.domain; 2 3 import java.io.Serializable; 4 import java.util.Date; 5 /** 6 * @author gacl 7 * 用戶實體類 8 */ 9 public class User implements Serializable { 10 11 private static final long serialVersionUID = -4313782718477229465L; 12 13 // 用戶ID 14 private String id; 15 // 用戶名 16 private String userName; 17 // 用戶密碼 18 private String userPwd; 19 // 用戶郵箱 20 private String email; 21 // 用戶生日 22 private Date birthday; 23 24 public String getId() { 25 return id; 26 } 27 28 public void setId(String id) { 29 this.id = id; 30 } 31 32 public String getUserName() { 33 return userName; 34 } 35 36 public void setUserName(String userName) { 37 this.userName = userName; 38 } 39 40 public String getUserPwd() { 41 return userPwd; 42 } 43 44 public void setUserPwd(String userPwd) { 45 this.userPwd = userPwd; 46 } 47 48 public String getEmail() { 49 return email; 50 } 51 52 public void setEmail(String email) { 53 this.email = email; 54 } 55 56 public Date getBirthday() { 57 return birthday; 58 } 59 60 public void setBirthday(Date birthday) { 61 this.birthday = birthday; 62 } 63 }
3.2、開發數據訪問層(dao、dao.impl)
在me.gacl.dao包下創建一個IUserDao接口類,對於開發接口類,我習慣以字母I作類的前綴,這樣一眼就看出當前這個類是一個接口,這也算是一種良好的開發習慣吧,通過看類名就可以方便區分出是接口還是具體的實現類。
IUserDao接口的具體代碼如下:
1 package me.gacl.dao; 2 3 import me.gacl.domain.User; 4 5 public interface IUserDao { 6 7 /** 8 * 根據用戶名和密碼來查找用戶 9 * @param userName 10 * @param userPwd 11 * @return 查到到的用戶 12 */ 13 User find(String userName, String userPwd); 14 15 /** 16 * 添加用戶 17 * @param user 18 */ 19 void add(User user); 20 21 /**根據用戶名來查找用戶 22 * @param userName 23 * @return 查到到的用戶 24 */ 25 User find(String userName); 26 }
對於接口中的方法定義,這個只能是根據具體的業務來分析需要定義哪些方法了,但是無論是多么復雜的業務,都離不開基本的CRUD(增刪改查)操作,Dao層是直接和數據庫交互的,所以Dao層的接口一般都會有增刪改查這四種操作的相關方法。
在me.gacl.dao.impl包下創建一個UserDaoImpl類
UserDaoImpl類是IUserDao接口的具體實現類,對於接口的實現類命名方式,我習慣以"接口名(去除前綴I)+impl"形式或者"接口名+impl"形式來命名:IUserDao(接口)→UserDaoImpl(實現類)或者IUserDao(接口)→IUserDaoImpl(實現類),這也算是一些個人的編程習慣吧,平時看到的代碼大多數都是以這兩種形式中的一種來來命名接口的具體實現類的,反正就是要能夠一眼看出接口對應的實現類是哪一個就可以了。
UserDaoImpl類的具體代碼如下:
1 package me.gacl.dao.impl; 2 3 import java.text.SimpleDateFormat; 4 import org.dom4j.Document; 5 import org.dom4j.Element; 6 import me.gacl.dao.IUserDao; 7 import me.gacl.domain.User; 8 import me.gacl.util.XmlUtils; 9 10 /** 11 * IUserDao接口的實現類 12 * @author gacl 13 */ 14 public class UserDaoImpl implements IUserDao { 15 16 @Override 17 public User find(String userName, String userPwd) { 18 try{ 19 Document document = XmlUtils.getDocument(); 20 //使用XPath表達式來操作XML節點 21 Element e = (Element) document.selectSingleNode("//user[@userName='"+userName+"' and @userPwd='"+userPwd+"']"); 22 if(e==null){ 23 return null; 24 } 25 User user = new User(); 26 user.setId(e.attributeValue("id")); 27 user.setEmail(e.attributeValue("email")); 28 user.setUserPwd(e.attributeValue("userPwd")); 29 user.setUserName(e.attributeValue("userName")); 30 String birth = e.attributeValue("birthday"); 31 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 32 user.setBirthday(sdf.parse(birth)); 33 34 return user; 35 36 }catch (Exception e) { 37 throw new RuntimeException(e); 38 } 39 } 40 41 @SuppressWarnings("deprecation") 42 @Override 43 public void add(User user) { 44 try{ 45 Document document = XmlUtils.getDocument(); 46 Element root = document.getRootElement(); 47 Element user_node = root.addElement("user"); //創建user結點,並掛到root 48 user_node.setAttributeValue("id", user.getId()); 49 user_node.setAttributeValue("userName", user.getUserName()); 50 user_node.setAttributeValue("userPwd", user.getUserPwd()); 51 user_node.setAttributeValue("email", user.getEmail()); 52 53 SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); 54 user_node.setAttributeValue("birthday", sdf.format(user.getBirthday())); 55 56 XmlUtils.write2Xml(document); 57 58 }catch (Exception e) { 59 throw new RuntimeException(e); 60 } 61 } 62 63 @Override 64 public User find(String userName) { 65 try{ 66 Document document = XmlUtils.getDocument(); 67 Element e = (Element) document.selectSingleNode("//user[@userName='"+userName+"']"); 68 if(e==null){ 69 return null; 70 } 71 User user = new User(); 72 user.setId(e.attributeValue("id")); 73 user.setEmail(e.attributeValue("email")); 74 user.setUserPwd(e.attributeValue("userPwd")); 75 user.setUserName(e.attributeValue("userName")); 76 String birth = e.attributeValue("birthday"); 77 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 78 user.setBirthday(sdf.parse(birth)); 79 80 return user; 81 82 }catch (Exception e) { 83 throw new RuntimeException(e); 84 } 85 } 86 87 }
3.3、開發service層(service層對web層提供所有的業務服務)
在me.gacl.service包中創建IUserService接口類
IUserService接口的具體代碼如下:
1 package me.gacl.service; 2 3 import me.gacl.domain.User; 4 import me.gacl.exception.UserExistException; 5 6 public interface IUserService { 7 8 /** 9 * 提供注冊服務 10 * @param user 11 * @throws UserExistException 12 */ 13 void registerUser(User user) throws UserExistException; 14 15 /** 16 * 提供登錄服務 17 * @param userName 18 * @param userPwd 19 * @return 20 */ 21 User loginUser(String userName, String userPwd); 22 }
在me.gacl.service.impl包中創建UserServiceImpl類
UserServiceImpl類為IUserService接口的具體實現類,具體代碼如下:
1 package me.gacl.service.impl; 2 3 import me.gacl.dao.IUserDao; 4 import me.gacl.dao.impl.UserDaoImpl; 5 import me.gacl.domain.User; 6 import me.gacl.exception.UserExistException; 7 import me.gacl.service.IUserService; 8 9 public class UserServiceImpl implements IUserService { 10 11 private IUserDao userDao = new UserDaoImpl(); 12 13 @Override 14 public void registerUser(User user) throws UserExistException { 15 if (userDao.find(user.getUserName())!=null) { 16 //checked exception 17 //unchecked exception 18 //這里拋編譯時異常的原因:是我想上一層程序處理這個異常,以給用戶一個友好提示 19 throw new UserExistException("注冊的用戶名已存在!!!"); 20 } 21 userDao.add(user); 22 } 23 24 @Override 25 public User loginUser(String userName, String userPwd) { 26 return userDao.find(userName, userPwd); 27 } 28 29 }
3.4、開發web層
3.4.1、 開發注冊功能
1、在me.gacl.web.UI包下寫一個RegisterUIServlet為用戶提供注冊界面
RegisterUIServlet收到用戶請求后,就跳到register.jsp
RegisterUIServlet的代碼如下:
1 package me.gacl.web.UI; 2 3 import java.io.IOException; 4 import javax.servlet.ServletException; 5 import javax.servlet.http.HttpServlet; 6 import javax.servlet.http.HttpServletRequest; 7 import javax.servlet.http.HttpServletResponse; 8 /** 9 * @author gacl 10 * 為用戶提供注冊的用戶界面的Servlet 11 * RegisterUIServlet負責為用戶輸出注冊界面 12 * 當用戶訪問RegisterUIServlet時,就跳轉到WEB-INF/pages目錄下的register.jsp頁面 13 */ 14 public class RegisterUIServlet extends HttpServlet { 15 16 public void doGet(HttpServletRequest request, HttpServletResponse response) 17 throws ServletException, IOException { 18 request.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(request, response); 19 } 20 21 public void doPost(HttpServletRequest request, HttpServletResponse response) 22 throws ServletException, IOException { 23 doGet(request, response); 24 } 25 26 }
2、在/WEB-INF/pages/目錄下編寫用戶注冊的jsp頁面register.jsp
凡是位於WEB-INF目錄下的jsp頁面是無法直接通過URL地址直接訪問的,
在開發中如果項目中有一些敏感web資源不想被外界直接訪問,那么可以考慮將這些敏感的web資源放到WEB-INF目錄下,這樣就可以禁止外界直接通過URL來訪問了。
register.jsp頁面的代碼如下:
1 <%@ page language="java" pageEncoding="UTF-8"%> 2 <!DOCTYPE HTML> 3 <html> 4 <head> 5 <title>用戶注冊</title> 6 </head> 7 8 <body style="text-align: center;"> 9 <form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post"> 10 <table width="60%" border="1"> 11 <tr> 12 <td>用戶名</td> 13 <td> 14 15 <input type="text" name="userName"> 16 </td> 17 </tr> 18 <tr> 19 <td>密碼</td> 20 <td> 21 <input type="password" name="userPwd"> 22 </td> 23 </tr> 24 <tr> 25 <td>確認密碼</td> 26 <td> 27 <input type="password" name="confirmPwd"> 28 </td> 29 </tr> 30 <tr> 31 <td>郵箱</td> 32 <td> 33 <input type="text" name="email"> 34 </td> 35 </tr> 36 <tr> 37 <td>生日</td> 38 <td> 39 <input type="text" name="birthday"> 40 </td> 41 </tr> 42 <tr> 43 <td> 44 <input type="reset" value="清空"> 45 </td> 46 <td> 47 <input type="submit" value="注冊"> 48 </td> 49 </tr> 50 </table> 51 </form> 52 </body> 53 </html>
register.jsp中的<form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post">指明表單提交后,交給RegisterServlet進行處理
3、在me.gacl.web.controller包下編寫用於處理用戶注冊的RegisterServlet
RegisterServlet擔任着以下幾個職責:
1、接收客戶端提交到服務端的表單數據。
2、校驗表單數據的合法性,如果校驗失敗跳回到register.jsp,並回顯錯誤信息。
3、如果校驗通過,調用service層向數據庫中注冊用戶。
為了方便RegisterServlet接收表單數據和校驗表單數據,在此我設計一個用於校驗注冊表單數據RegisterFormbean,再寫WebUtils工具類,封裝客戶端提交的表單數據到formbean中。
在me.gacl.web.formbean包下創建一個用於校驗注冊表單數據RegisterFormbean
RegisterFormbean代碼如下:
1 package me.gacl.web.formbean; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; 7 8 /** 9 * 封裝的用戶注冊表單bean,用來接收register.jsp中的表單輸入項的值 10 * RegisterFormBean中的屬性與register.jsp中的表單輸入項的name一一對應 11 * RegisterFormBean的職責除了負責接收register.jsp中的表單輸入項的值之外還擔任着校驗表單輸入項的值的合法性 12 * @author gacl 13 * 14 */ 15 public class RegisterFormBean { 16 17 //RegisterFormBean中的屬性與register.jsp中的表單輸入項的name一一對應 18 //<input type="text" name="userName"/> 19 private String userName; 20 //<input type="password" name="userPwd"/> 21 private String userPwd; 22 //<input type="password" name="confirmPwd"/> 23 private String confirmPwd; 24 //<input type="text" name="email"/> 25 private String email; 26 //<input type="text" name="birthday"/> 27 private String birthday; 28 29 30 /** 31 * 存儲校驗不通過時給用戶的錯誤提示信息 32 */ 33 private Map<String, String> errors = new HashMap<String, String>(); 34 35 public Map<String, String> getErrors() { 36 return errors; 37 } 38 39 public void setErrors(Map<String, String> errors) { 40 this.errors = errors; 41 } 42 43 /* 44 * validate方法負責校驗表單輸入項 45 * 表單輸入項校驗規則: 46 * private String userName; 用戶名不能為空,並且要是3-8的字母 abcdABcd 47 * private String userPwd; 密碼不能為空,並且要是3-8的數字 48 * private String confirmPwd; 兩次密碼要一致 49 * private String email; 可以為空,不為空要是一個合法的郵箱 50 * private String birthday; 可以為空,不為空時,要是一個合法的日期 51 */ 52 public boolean validate() { 53 54 boolean isOk = true; 55 56 if (this.userName == null || this.userName.trim().equals("")) { 57 isOk = false; 58 errors.put("userName", "用戶名不能為空!!"); 59 } else { 60 if (!this.userName.matches("[a-zA-Z]{3,8}")) { 61 isOk = false; 62 errors.put("userName", "用戶名必須是3-8位的字母!!"); 63 } 64 } 65 66 if (this.userPwd == null || this.userPwd.trim().equals("")) { 67 isOk = false; 68 errors.put("userPwd", "密碼不能為空!!"); 69 } else { 70 if (!this.userPwd.matches("\\d{3,8}")) { 71 isOk = false; 72 errors.put("userPwd", "密碼必須是3-8位的數字!!"); 73 } 74 } 75 76 // private String password2; 兩次密碼要一致 77 if (this.confirmPwd != null) { 78 if (!this.confirmPwd.equals(this.userPwd)) { 79 isOk = false; 80 errors.put("confirmPwd", "兩次密碼不一致!!"); 81 } 82 } 83 84 // private String email; 可以為空,不為空要是一個合法的郵箱 85 if (this.email != null && !this.email.trim().equals("")) { 86 if (!this.email.matches("\\w+@\\w+(\\.\\w+)+")) { 87 isOk = false; 88 errors.put("email", "郵箱不是一個合法郵箱!!"); 89 } 90 } 91 92 // private String birthday; 可以為空,不為空時,要是一個合法的日期 93 if (this.birthday != null && !this.birthday.trim().equals("")) { 94 try { 95 DateLocaleConverter conver = new DateLocaleConverter(); 96 conver.convert(this.birthday); 97 } catch (Exception e) { 98 isOk = false; 99 errors.put("birthday", "生日必須要是一個日期!!"); 100 } 101 } 102 103 return isOk; 104 } 105 106 public String getUserName() { 107 return userName; 108 } 109 110 public void setUserName(String userName) { 111 this.userName = userName; 112 } 113 114 public String getUserPwd() { 115 return userPwd; 116 } 117 118 public void setUserPwd(String userPwd) { 119 this.userPwd = userPwd; 120 } 121 122 public String getConfirmPwd() { 123 return confirmPwd; 124 } 125 126 public void setConfirmPwd(String confirmPwd) { 127 this.confirmPwd = confirmPwd; 128 } 129 130 public String getEmail() { 131 return email; 132 } 133 134 public void setEmail(String email) { 135 this.email = email; 136 } 137 138 public String getBirthday() { 139 return birthday; 140 } 141 142 public void setBirthday(String birthday) { 143 this.birthday = birthday; 144 } 145 }
在me.gacl.util包下創建一個WebUtils工具類,該工具類的功能就是封裝客戶端提交的表單數據到formbean中
1 package me.gacl.util; 2 3 import java.util.Enumeration; 4 import java.util.UUID; 5 import javax.servlet.http.HttpServletRequest; 6 import org.apache.commons.beanutils.BeanUtils; 7 8 /** 9 * @author gacl 10 * 把request對象中的請求參數封裝到bean中 11 */ 12 public class WebUtils { 13 14 /** 15 * 將request對象轉換成T對象 16 * @param request 17 * @param clazz 18 * @return 19 */ 20 public static <T> T request2Bean(HttpServletRequest request,Class<T> clazz){ 21 try{ 22 T bean = clazz.newInstance(); 23 Enumeration<String> e = request.getParameterNames(); 24 while(e.hasMoreElements()){ 25 String name = (String) e.nextElement(); 26 String value = request.getParameter(name); 27 BeanUtils.setProperty(bean, name, value); 28 } 29 return bean; 30 }catch (Exception e) { 31 throw new RuntimeException(e); 32 } 33 } 34 35 /** 36 * 生成UUID 37 * @return 38 */ 39 public static String makeId(){ 40 return UUID.randomUUID().toString(); 41 } 42 43 }
最后看一下負責處理用戶注冊的RegisterServlet完整代碼:
1 package me.gacl.web.controller; 2 3 import java.io.IOException; 4 import java.util.Date; 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 import org.apache.commons.beanutils.BeanUtils; 10 import org.apache.commons.beanutils.ConvertUtils; 11 import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; 12 import me.gacl.domain.User; 13 import me.gacl.exception.UserExistException; 14 import me.gacl.service.IUserService; 15 import me.gacl.service.impl.UserServiceImpl; 16 import me.gacl.util.WebUtils; 17 import me.gacl.web.formbean.RegisterFormBean; 18 /** 19 * 處理用戶注冊的Servlet 20 * @author gacl 21 * 22 */ 23 public class RegisterServlet extends HttpServlet { 24 25 public void doGet(HttpServletRequest request, HttpServletResponse response) 26 throws ServletException, IOException { 27 //將客戶端提交的表單數據封裝到RegisterFormBean對象中 28 RegisterFormBean formbean = WebUtils.request2Bean(request,RegisterFormBean.class); 29 //校驗用戶注冊填寫的表單數據 30 if (formbean.validate() == false) {//如果校驗失敗 31 //將封裝了用戶填寫的表單數據的formbean對象發送回register.jsp頁面的form表單中進行顯示 32 request.setAttribute("formbean", formbean); 33 //校驗失敗就說明是用戶填寫的表單數據有問題,那么就跳轉回register.jsp 34 request.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(request, response); 35 return; 36 } 37 38 User user = new User(); 39 try { 40 // 注冊字符串到日期的轉換器 41 ConvertUtils.register(new DateLocaleConverter(), Date.class); 42 BeanUtils.copyProperties(user, formbean);//把表單的數據填充到javabean中 43 user.setId(WebUtils.makeId());//設置用戶的Id屬性 44 IUserService service = new UserServiceImpl(); 45 //調用service層提供的注冊用戶服務實現用戶注冊 46 service.registerUser(user); 47 String message = String.format( 48 "注冊成功!!3秒后為您自動跳到登錄頁面!!<meta http-equiv='refresh' content='3;url=%s'/>", 49 request.getContextPath()+"/servlet/LoginUIServlet"); 50 request.setAttribute("message",message); 51 request.getRequestDispatcher("/message.jsp").forward(request,response); 52 53 } catch (UserExistException e) { 54 formbean.getErrors().put("userName", "注冊用戶已存在!!"); 55 request.setAttribute("formbean", formbean); 56 request.getRequestDispatcher("/WEB-INF/pages/register.jsp").forward(request, response); 57 } catch (Exception e) { 58 e.printStackTrace(); // 在后台記錄異常 59 request.setAttribute("message", "對不起,注冊失敗!!"); 60 request.getRequestDispatcher("/message.jsp").forward(request,response); 61 } 62 } 63 64 public void doPost(HttpServletRequest request, HttpServletResponse response) 65 throws ServletException, IOException { 66 doGet(request, response); 67 } 68 69 }
用戶注冊時如果填寫的表單數據校驗不通過,那么服務器端就將一個存儲了錯誤提示消息和表單數據的formbean對象存儲到request對象中,然后發送回register.jsp頁面,因此我們需要在register.jsp頁面中取出request對象中formbean對象,然后將用戶填寫的表單數據重新回顯到對應的表單項上面,將出錯時的提示消息也顯示到form表單上面,讓用戶知道是哪些數據填寫不合法!
修改register.jsp頁面,代碼如下:
1 <%@ page language="java" pageEncoding="UTF-8"%> 2 <!DOCTYPE HTML> 3 <html> 4 <head> 5 <title>用戶注冊</title> 6 </head> 7 8 <body style="text-align: center;"> 9 <form action="${pageContext.request.contextPath}/servlet/RegisterServlet" method="post"> 10 <table width="60%" border="1"> 11 <tr> 12 <td>用戶名</td> 13 <td> 14 <%--使用EL表達式${}提取存儲在request對象中的formbean對象中封裝的表單數據(formbean.userName)以及錯誤提示消息(formbean.errors.userName)--%> 15 <input type="text" name="userName" value="${formbean.userName}">${formbean.errors.userName} 16 </td> 17 </tr> 18 <tr> 19 <td>密碼</td> 20 <td> 21 <input type="password" name="userPwd" value="${formbean.userPwd}">${formbean.errors.userPwd} 22 </td> 23 </tr> 24 <tr> 25 <td>確認密碼</td> 26 <td> 27 <input type="password" name="confirmPwd" value="${formbean.confirmPwd}">${formbean.errors.confirmPwd} 28 </td> 29 </tr> 30 <tr> 31 <td>郵箱</td> 32 <td> 33 <input type="text" name="email" value="${formbean.email}">${formbean.errors.email} 34 </td> 35 </tr> 36 <tr> 37 <td>生日</td> 38 <td> 39 <input type="text" name="birthday" value="${formbean.birthday}">${formbean.errors.birthday} 40 </td> 41 </tr> 42 <tr> 43 <td> 44 <input type="reset" value="清空"> 45 </td> 46 <td> 47 <input type="submit" value="注冊"> 48 </td> 49 </tr> 50 </table> 51 </form> 52 </body> 53 </html>
到此,用戶注冊功能就算是開發完成了!
下面測試一下開發好的用戶注冊功能:
輸入URL地址:http://localhost:8080/webmvcframework/servlet/RegisterUIServlet訪問register.jsp頁面,運行效果如下:
如果輸入的表單項不符合校驗規則,那么是無法進行注冊的,運行效果如下:
3.4.2、 開發登錄功能
1、在me.gacl.web.UI包下寫一個LoginUIServlet為用戶提供登錄界面
LoginUIServlet收到用戶請求后,就跳到login.jsp
LoginUIServlet的代碼如下:
1 package me.gacl.web.UI; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 /** 11 * @author gacl 12 * LoginUIServlet負責為用戶輸出登陸界面 13 * 當用戶訪問LoginUIServlet時,就跳轉到WEB-INF/pages目錄下的login.jsp頁面 14 */ 15 public class LoginUIServlet extends HttpServlet { 16 17 public void doGet(HttpServletRequest request, HttpServletResponse response) 18 throws ServletException, IOException { 19 20 request.getRequestDispatcher("/WEB-INF/pages/login.jsp").forward(request, response); 21 } 22 23 public void doPost(HttpServletRequest request, HttpServletResponse response) 24 throws ServletException, IOException { 25 doGet(request, response); 26 } 27 28 }
2、在/WEB-INF/pages/目錄下編寫用戶登錄的jsp頁面login.jsp
login.jsp頁面的代碼如下:
1 <%@ page language="java" pageEncoding="UTF-8"%> 2 <!DOCTYPE HTML> 3 <html> 4 <head> 5 <title>用戶登陸</title> 6 </head> 7 8 <body> 9 <form action="${pageContext.request.contextPath }/servlet/LoginServlet" method="post"> 10 用戶名:<input type="text" name="username"><br/> 11 密碼:<input type="password" name="password"><br/> 12 <input type="submit" value="登陸"> 13 </form> 14 </body> 15 </html>
login.jsp中的<form action="${pageContext.request.contextPath}/servlet/LoginServlet" method="post">指明表單提交后,交給LoginServlet進行處理。
3、在me.gacl.web.controller包下編寫用於處理用戶登錄的LoginServlet
LoginServlet的代碼如下:
1 package me.gacl.web.controller; 2 3 import java.io.IOException; 4 5 import javax.servlet.ServletException; 6 import javax.servlet.http.HttpServlet; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 10 import me.gacl.domain.User; 11 import me.gacl.service.IUserService; 12 import me.gacl.service.impl.UserServiceImpl; 13 14 /** 15 * 處理用戶登錄的servlet 16 * @author gacl 17 * 18 */ 19 public class LoginServlet extends HttpServlet { 20 21 public void doGet(HttpServletRequest request, HttpServletResponse response) 22 throws ServletException, IOException { 23 24 //獲取用戶填寫的登錄用戶名 25 String username = request.getParameter("username"); 26 //獲取用戶填寫的登錄密碼 27 String password = request.getParameter("password"); 28 29 IUserService service = new UserServiceImpl(); 30 //用戶登錄 31 User user = service.loginUser(username, password); 32 if(user==null){ 33 String message = String.format( 34 "對不起,用戶名或密碼有誤!!請重新登錄!2秒后為您自動跳到登錄頁面!!<meta http-equiv='refresh' content='2;url=%s'", 35 request.getContextPath()+"/servlet/LoginUIServlet"); 36 request.setAttribute("message",message); 37 request.getRequestDispatcher("/message.jsp").forward(request, response); 38 return; 39 } 40 //登錄成功后,就將用戶存儲到session中 41 request.getSession().setAttribute("user", user); 42 String message = String.format( 43 "恭喜:%s,登陸成功!本頁將在3秒后跳到首頁!!<meta http-equiv='refresh' content='3;url=%s'", 44 user.getUserName(), 45 request.getContextPath()+"/index.jsp"); 46 request.setAttribute("message",message); 47 request.getRequestDispatcher("/message.jsp").forward(request, response); 48 } 49 50 public void doPost(HttpServletRequest request, HttpServletResponse response) 51 throws ServletException, IOException { 52 doGet(request, response); 53 } 54 55 }
到此,用戶登錄的功能就算是開發完成了。
下面測試一下開發好的用戶登錄功能,輸入URL地址:http://localhost:8080/webmvcframework/servlet/LoginUIServlet訪問login.jsp頁面,輸入正確的用戶名和密碼進行登錄,運行效果如下:
如果輸入的用戶名和密碼錯誤,那么就無法登錄成功,運行效果如下:
3.4.3、 開發注銷功能
在me.gacl.web.controller包下編寫用於處理用戶注銷的LogoutServlet
LogoutServlet的代碼如下:
1 package me.gacl.web.controller; 2 3 import java.io.IOException; 4 import java.text.MessageFormat; 5 6 import javax.servlet.ServletException; 7 import javax.servlet.http.HttpServlet; 8 import javax.servlet.http.HttpServletRequest; 9 import javax.servlet.http.HttpServletResponse; 10 11 public class LogoutServlet extends HttpServlet { 12 13 public void doGet(HttpServletRequest request, HttpServletResponse response) 14 throws ServletException, IOException { 15 //移除存儲在session中的user對象,實現注銷功能 16 request.getSession().removeAttribute("user"); 17 //由於字符串中包含有單引號,在這種情況下使用MessageFormat.format方法拼接字符串時就會有問題 18 //MessageFormat.format方法只是把字符串中的單引號去掉,不會將內容填充到指定的占位符中 19 String tempStr1 = MessageFormat.format( 20 "注銷成功!!3秒后為您自動跳到登錄頁面!!<meta http-equiv='refresh' content='3;url={0}'/>", 21 request.getContextPath()+"/servlet/LoginUIServlet"); 22 System.out.println(tempStr1);//輸出結果:注銷成功!!3秒后為您自動跳到登錄頁面!!<meta http-equiv=refresh content=3;url={0}/> 23 System.out.println("---------------------------------------------------------"); 24 /** 25 * 要想解決"如果要拼接的字符串包含有單引號,那么MessageFormat.format方法就只是把字符串中的單引號去掉,不會將內容填充到指定的占位符中"這個問題, 26 * 那么可以需要使用單引號引起來的字符串中使用2個單引號引起來,例如:"<meta http-equiv=''refresh'' content=''3;url={0}''/>" 27 * 這樣MessageFormat.format("<meta http-equiv=''refresh'' content=''3;url={0}''/>","index.jsp")就可以正常返回 28 * <meta http-equiv=''refresh'' content=''3;url=index.jsp'/> 29 */ 30 String tempStr2 = MessageFormat.format( 31 "注銷成功!!3秒后為您自動跳到登錄頁面!!<meta http-equiv=''refresh'' content=''3;url={0}''/>", 32 request.getContextPath()+"/servlet/LoginUIServlet"); 33 /** 34 * 輸出結果: 35 * 注銷成功!!3秒后為您自動跳到登錄頁面!! 36 * <meta http-equiv='refresh' content='3;url=/webmvcframework/servlet/LoginUIServlet'/> 37 */ 38 System.out.println(tempStr2); 39 40 String message = String.format( 41 "注銷成功!!3秒后為您自動跳到登錄頁面!!<meta http-equiv='refresh' content='3;url=%s'/>", 42 request.getContextPath()+"/servlet/LoginUIServlet"); 43 request.setAttribute("message",message); 44 request.getRequestDispatcher("/message.jsp").forward(request, response); 45 } 46 47 public void doPost(HttpServletRequest request, HttpServletResponse response) 48 throws ServletException, IOException { 49 doGet(request, response); 50 } 51 52 }
用戶登錄成功后,會將登錄的用戶信息存儲在session中,所以我們要將存儲在session中的user刪除掉,這樣就可以實現用戶注銷了。
用戶登錄成功后就會跳轉到index.jsp頁面,在index.jsp頁面中放一個【退出登陸】按鈕,當點擊【退出登陸】按鈕時,就訪問LogoutServlet,將用戶注銷。
index.jsp的代碼如下:
1 <%@ page language="java" pageEncoding="UTF-8"%> 2 <%--為了避免在jsp頁面中出現java代碼,這里引入jstl標簽庫,利用jstl標簽庫提供的標簽來做一些邏輯判斷處理 --%> 3 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 4 <!DOCTYPE HTML> 5 <html> 6 <head> 7 <title>首頁</title> 8 <script type="text/javascript"> 9 function doLogout(){ 10 //訪問LogoutServlet注銷當前登錄的用戶 11 window.location.href="${pageContext.request.contextPath}/servlet/LogoutServlet"; 12 } 13 </script> 14 </head> 15 16 <body> 17 <h1>孤傲蒼狼的網站</h1> 18 <hr/> 19 <c:if test="${user==null}"> 20 <a href="${pageContext.request.contextPath}/servlet/RegisterUIServlet" target="_blank">注冊</a> 21 <a href="${pageContext.request.contextPath}/servlet/LoginUIServlet">登陸</a> 22 </c:if> 23 <c:if test="${user!=null}"> 24 歡迎您:${user.userName} 25 <input type="button" value="退出登陸" onclick="doLogout()"> 26 </c:if> 27 <hr/> 28 </body> 29 </html>
測試開發好的注銷功能,效果如下:
到此,所有的功能都開發完成了,測試也通過了。
四、開發總結
通過這個小例子,可以了解到mvc分層架構的項目搭建,在平時的項目開發中,也都是按照如下的順序來進行開發的:
1、搭建開發環境
1.1 創建web項目
1.2 導入項目所需的開發包
1.3 創建程序的包名,在java中是以包來體現項目的分層架構的
2、開發domain
把一張要操作的表當成一個VO類(VO類只定義屬性以及屬性對應的get和set方法,沒有涉及到具體業務的操作方法),VO表示的是值對象,通俗地說,就是把表中的每一條記錄當成一個對象,表中的每一個字段就作為這個對象的屬性。每往表中插入一條記錄,就相當於是把一個VO類的實例對象插入到數據表中,對數據表進行操作時,都是直接把一個VO類的對象寫入到表中,一個VO類對象就是一條記錄。每一個VO對象可以表示一張表中的一行記錄,VO類的名稱要和表的名稱一致或者對應。
3、開發dao
3.1 DAO操作接口:每一個DAO操作接口規定了,一張表在一個項目中的具體操作方法,此接口的名稱最好按照如下格式編寫:“I表名稱Dao”。
├DAO接口里面的所有方法按照以下的命名編寫:
├更新數據庫:doXxx()
├查詢數據庫:findXxx()或getXxx()
3.2 DAO操作接口的實現類:實現類中完成具體的增刪改查操作
├此實現類完成的只是數據庫中最核心的操作,並沒有專門處理數據庫的打開和關閉,因為這些操作與具體的業務操作無關。
4、開發service(service 對web層提供所有的業務服務)
5、開發web層
點擊此處下載項目源碼