在Web開發模式中,有兩個主要的開發結構,稱爲模式一(Mode I)和模式二(Mode II).html
首先咱們來理清一些概念吧:java
模式一指的就是在開發中將顯示層、控制層、數據層的操做統一交給JSP或者JavaBean來進行處理!web
模式一有兩種狀況:數據庫
咱們使用JavaBean+JSP開發一個簡易的計算器吧,效果如圖下:設計模式
public class Calculator { private double firstNum; private double secondNum; private char Operator = '+'; private double result; //JavaBean提供了計算的功能 public void calculate() { switch (this.Operator) { case '+': this.result = this.firstNum + this.secondNum; break; case '-': this.result = this.firstNum - this.secondNum; break; case '*': this.result = this.firstNum * this.secondNum; break; case '/': if (this.secondNum == 0) { throw new RuntimeException("除數不能爲0"); } this.result = this.firstNum / this.secondNum; break; default: throw new RuntimeException("傳入的字符非法!"); } } public double getFirstNum() { return firstNum; } public void setFirstNum(double firstNum) { this.firstNum = firstNum; } public double getSecondNum() { return secondNum; } public void setSecondNum(double secondNum) { this.secondNum = secondNum; } public char getOperator() { return Operator; } public void setOperator(char operator) { Operator = operator; } public double getResult() { return result; } public void setResult(double result) { this.result = result; } }
<%--開發用戶界面--%> <form action="/zhongfucheng/1.jsp" method="post"> <table border="1"> <tr> <td colspan="2">簡單計數器</td> <td></td> </tr> <tr> <td>第一個參數:</td> <td><input type="text" name="firstNum"></td> </tr> <tr> <td>運算符</td> <td> <select name="operator"> <option value="+">+</option> <option value="-">-</option> <option value="*">*</option> <option value="/">/</option> </select> </td> </tr> <tr> <td>第二個參數:</td> <td><input type="text " name="secondNum"></td> </tr> <tr> <td colspan="2"><input type="submit" value="提交"></td> <td></td> </tr> </table> </form>
<%--獲取獲得Bean對象--%> <jsp:useBean id="calculator" class="domain.Calculator" scope="page"/> <%--設置Bean對象的數據--%> <jsp:setProperty name="calculator" property="*"/> <%--調用Caculator的方法計算出值--%> <jsp:scriptlet> calculator.calculate(); </jsp:scriptlet> <%--得出的結果:--%> <c:out value="計算得出的結果是:"/> <jsp:getProperty name="calculator" property="firstNum"/> <jsp:getProperty name="calculator" property="operator"/> <jsp:getProperty name="calculator" property="secondNum"/> <c:out value="="/> <jsp:getProperty name="calculator" property="result"/>
開發這個簡易的計算器,只用了一個JSP頁面和一個JavaBean完成!服務器
總的來講,Mode I 適合小型的開發,複雜程序低的開發,由於Mode I 的特色就是開發速度快,但在進行維護的時候就要付出更大的代價!微信
Mode II 中全部的開發都是以Servlet爲主體展開的,由Servlet接收全部的客戶端請求,而後根據請求調用相對應的JavaBean,並全部的顯示結果交給JSP完成!,也就是俗稱的MVC設計模式!dom
MVC設計模式:jsp
咱們使用MVC模式開發一個簡單的用戶登錄註冊的案例吧!做爲一個簡單的用戶登錄註冊,這裏就直接使用XML文檔看成小型數據庫吧!ide
private int id; private String username; private String password; private String email; private Date birthday; //....各類setter、getter
這個根據業務來開發,咱們是登錄註冊,那應該提供什麼功能呢?註冊(外界傳遞一個User對象進來,我能夠在XML文檔多一條信息)。登錄(外界傳遞用戶名和密碼過來,我就在XML文檔中查找有沒該用戶名和密碼,若是有就返回一個User對象)
3.1登錄功能:
//外界傳遞用戶名和密碼進來,我要在XML文檔中查找是否有該條記錄 public User find(String username, String password) { //獲得XML文檔的流對象 InputStream inputStream = UserImplXML.class.getClassLoader().getResourceAsStream("user.xml"); //獲得dom4j的解析器對象 SAXReader saxReader = new SAXReader(); try { //解析XML文檔 Document document = saxReader.read(path); //使用XPATH技術,查找XML文檔中是否有傳遞進來的username和password Element element = (Element) document.selectSingleNode("//user[@username='" + username + "' and@password='" + password + "']"); if (element == null) { return null; } //若是有,就把XML查出來的節點信息封裝到User對象,返回出去 User user = new User(); user.setId(Integer.parseInt(element.attributeValue("id"))); user.setUsername(element.attributeValue("username")); user.setPassword(element.attributeValue("password")); user.setEmail(element.attributeValue("email")); //生日就須要轉換一下了,XML文檔保存的是字符串,User對象須要的是Date類型 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd"); Date birthday = simpleDateFormat.parse(element.attributeValue("birthday")); user.setBirthday(birthday); //返回User對象出去 return user; } catch (DocumentException e) { e.printStackTrace(); throw new RuntimeException("初始化時候出錯啦!"); } catch (ParseException e) { e.printStackTrace(); throw new RuntimeException("查詢的時候出錯啦!"); } }
private String username = "zhongfucheng"; private String password = "123"; @Test public void testLogin() { UserImplXML userImplXML = new UserImplXML(); User user = userImplXML.find(username, password); System.out.println(user.getBirthday()); System.out.println(user.getEmail()); System.out.println(user.getId()); System.out.println(user.getUsername()); System.out.println(user.getPassword()); }
3.2註冊功能
//註冊功能,外界傳遞一個User對象進來。我就在XML文檔中添加一條信息 public void register(User user) { //獲取XML文檔路徑! String path = UserImplXML.class.getClassLoader().getResource("user.xml").getPath(); try { //獲取dom4j的解析器,解析XML文檔 SAXReader saxReader = new SAXReader(); Document document = saxReader.read(path); //在XML文檔中建立新的節點 Element newElement = DocumentHelper.createElement("user"); newElement.addAttribute("id", String.valueOf(user.getId())); newElement.addAttribute("username", user.getUsername()); newElement.addAttribute("email", user.getEmail()); newElement.addAttribute("password", user.getPassword()); //日期返回的是指定格式的日期 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd"); String date = simpleDateFormat.format(user.getBirthday()); newElement.addAttribute("birthday",date); //把新建立的節點增長到父節點上 document.getRootElement().add(newElement); //把XML內容中文檔的內容寫到硬盤文件上 OutputFormat outputFormat = OutputFormat.createPrettyPrint(); outputFormat.setEncoding("UTF-8"); XMLWriter xmlWriter = new XMLWriter(new FileWriter(path),outputFormat); xmlWriter.write(document); xmlWriter.close(); } catch (DocumentException e) { e.printStackTrace(); throw new RuntimeException("註冊的時候出錯了!!!"); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("註冊的時候出錯了!!!"); } }
@Test public void testRegister() { UserImplXML userImplXML = new UserImplXML(); //這裏我爲了測試的方便,就添加一個帶5個參數的構造函數了! User user = new User(10, "nihao", "123", "sina@qq.com", new Date()); userImplXML.register(user); }
service層的開發就很是簡單了!上面已經說了,service層就是:將多個原子性的DAO操做進行組合,組合成一個完整的業務邏輯。簡單來講:對web層提供全部的業務服務的!
在邏輯代碼不是很是複雜的狀況下,咱們能夠沒有service層的,這裏仍是演示一下吧!
public class UserServiceXML { //Service層就是調用Dao層的方法,咱們就直接在類中建立Dao層的對象了 UserDao userImplXML = new UserImplXML(); public void register(User user) { userImplXML.register(user); } public void login(String username, String password) { userImplXML.find(username, password); } }
public class RegisterUIServlet extends javax.servlet.http.HttpServlet { protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { //直接跳轉到顯示註冊界面的JSP request.getRequestDispatcher("/WEB-INF/register.jsp").forward(request, response); } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { this.doPost(request, response); } }
<h1>歡迎來到註冊界面!</h1> <%--提交給處理註冊的處理Servlet--%> <form method="post" action="${pageContext.request.contextPath}/RegisterServlet"> <table> <%--對於id來說,是服務器分配的!不須要用戶本身輸入--%> <tr> <td>用戶名</td> <td> <input type="text " name="username"> </td> </tr> <tr> <td>密碼</td> <td> <input type="text" name="password"> </td> </tr> <tr> <td>確認密碼</td> <td> <input type="text" name="password"> </td> </tr> <tr> <td>郵箱</td> <td> <input type="text" name="email"> </td> </tr> <tr> <td>生日</td> <td> <input type="text " name="birethday"> </td> </tr> <tr> <td> <input type="submit" value="提交"> </td> <td> <input type="reset" value="重置!"> </td> </tr> </table> </form>
//首先要接受Parameter的參數,封裝到User裏面去 String username = request.getParameter("username"); String password = request.getParameter("password"); //......若是參數過多,咱們就要寫好多好多相似的代碼了...
/* * 將Parameter參數的數據封裝到Bean中,爲了外邊不用強轉,這裏就使用泛型了! * * @request 因爲要獲取的是Parameter參數的信息,因此須要有request對象 * @tClass 自己是不知道封裝什麼對象的,因此用class * * */ public static <T> T request2Bean(HttpServletRequest httpServletRequest, Class<T> tClass) { try { //建立tClass的對象 T bean = tClass.newInstance(); //獲取獲得Parameter中所有的參數的名字 Enumeration enumeration = httpServletRequest.getParameterNames(); //遍歷上邊獲取獲得的集合 while (enumeration.hasMoreElements()) { //獲取獲得每個帶過來參數的名字 String name = (String) enumeration.nextElement(); //獲取獲得值 String value = httpServletRequest.getParameter(name); //把數據封裝到Bean對象中 BeanUtils.setProperty(bean, name, value); } return bean; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("封裝數據到Bean對象中出錯了!"); } }
對於日期而言,須要一個日期轉換器。當BeanUtils的setProperty()方法檢測到日期時,會自動調用日期轉換器對日期進行轉換,從而實現封裝!
因而乎,就在上面的方法中添加如下一句代碼
//日期轉換器 ConvertUtils.register(new DateLocaleConverter(), Date.class);
/*生成ID*/ public static int makeId() { return Integer.parseInt(UUID.randomUUID().toString()); }
User user = WebUtils.request2Bean(request, User.class); user.setId(WebUtils.makeId()); //調用service層的註冊方法,實現註冊 ServiceBussiness serviceBussiness = new UserServiceXML(); serviceBussiness.register(user);
上面的代碼是不夠完善的(沒有校驗用戶輸入的信息、註冊成功或失敗都沒有給出提示..等等)
下面,咱們來校驗用戶輸入的信息吧,若是用戶輸入的信息不合法,就直接跳轉回註冊的界面。
剛纔咱們是用BeanUtils把Parameter的信息所有直接封裝到User對象中,但如今我想要驗證用戶提交表單的數據,也應該把表單的數據用一個對象保存着【面向對象的思想、封裝、重用】
流程是這樣子的:當用戶提交表單數據的時候,就把表單數據封裝到咱們設計的表單對象上,調用表單對象的方法,驗證數據是否合法!
好了,咱們來開發一個表單的對象吧,最重要的是怎麼填寫validate()方法!!
public class FormBean { //表單提交過來的數據全都是String類型的,birthday也不例外! private String username; private String password; private String password2; private String email; private String birthday; /*用於判斷表單提交過來的數據是否合法*/ public boolean validate() { return false; } //......各類setter、getter方法 }
public boolean validate() { //用戶名不能爲空,而且要是3-8的字符 abcdABcd if (this.username == null || this.username.trim().equals("")) { return false; } else { if (!this.username.matches("[a-zA-Z]{3,8}")) { return false; } } //密碼不能爲空,而且要是3-8的數字 if (this.password == null || this.password.trim().equals("")) { return false; } else { if (!this.password.matches("\\d{3,8}")) { return false; } } //兩次密碼要一致 if (this.password2 != null && !this.password2.trim().equals("")) { if (!this.password2.equals(this.password)) { return false; } } //郵箱能夠爲空,若是爲空就必須合法 if (this.email != null && !this.email.trim().equals("")) { if (!this.email.matches("\\w+@\\w+(\\.\\w+)+")) { System.out.println("郵箱錯誤了!"); return false; } } //日期能夠爲空,若是爲空就必須合法 if (this.birthday != null && !this.birthday.trim().equals("")) { try { DateLocaleConverter dateLocaleConverter = new DateLocaleConverter(); dateLocaleConverter.convert(this.birthday); } catch (Exception e) { System.out.println("日期錯誤了!"); return false; } } //若是上面都沒有執行,那麼就是合法的了,返回true return true; }
//將表單的數據封裝到formBean中 FormBean formBean = WebUtils.request2Bean(request, FormBean.class); //驗證表單的數據是否合法,若是不合法就跳轉回去註冊的頁面 if(formBean.validate()==false){ request.getRequestDispatcher("/WEB-INF/register.jsp").forward(request, response); return; } try { //將表單的數據封裝到User對象中 User user = WebUtils.request2Bean(request, User.class); user.setId(WebUtils.makeId()); //調用service層的註冊方法,實現註冊 ServiceBussiness serviceBussiness = new UserServiceXML(); serviceBussiness.register(user); } catch (Exception e) { e.printStackTrace(); }
它拋出了錯誤!緣由也很是簡單:表單數據提交給Servlet,Servlet將表單的數據(Parameter中的數據)用BeanUtils封裝到User對象中,當封裝到日期的時候,發現日期爲null,沒法轉換成日期對象!
那咱們如今要怎麼解決呢?
首先咱們要明確:由於咱們在設定的時候,已經容許了email和birthday能夠爲空,那麼在DAO層就應該有相應的邏輯判斷email和birthday是否爲空!
if (user.getEmail() == null) { newElement.addAttribute("email", ""); } else { newElement.addAttribute("email", user.getEmail()); } //若是不是空才格式化信息 if (user.getBirthday() != null) { //日期返回的是指定格式的日期 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); String date = simpleDateFormat.format(user.getBirthday()); newElement.addAttribute("birthday", date); } else { newElement.addAttribute("birthday", ""); }
解決辦法:
public static <T> T request2Bean(HttpServletRequest httpServletRequest, Class<T> tClass) { try { //建立tClass的對象 T bean = tClass.newInstance(); //獲取獲得Parameter中所有的參數的名字 Enumeration enumeration = httpServletRequest.getParameterNames(); //日期轉換器 ConvertUtils.register(new DateLocaleConverter(), Date.class); //遍歷上邊獲取獲得的集合 while (enumeration.hasMoreElements()) { //獲取獲得每個帶過來參數的名字 String name = (String) enumeration.nextElement(); //獲取獲得值 String value = httpServletRequest.getParameter(name); //若是Parameter中的數據爲"",那麼我就不封裝到User對象裏邊去!執行下一次循環 if (value == "") { continue; } else { //把數據封裝到Bean對象中 BeanUtils.setProperty(bean, name, value); } } return bean; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("封裝數據到Bean對象中出錯了!"); } }
將數據封裝到User對象中還有另一個辦法:
//將表單的數據封裝到formBean中 FormBean formBean = WebUtils.request2Bean(request, FormBean.class); //驗證表單的數據是否合法,若是不合法就跳轉回去註冊的頁面 if(formBean.validate()==false){ request.getRequestDispatcher("/WEB-INF/register.jsp").forward(request, response); return; } try { //這是第一種-------------------------- /*User user = new User(); user.setId(WebUtils.makeId()); BeanUtils.copyProperties(user,formBean);*/ //------------------------------------------ //這是第二種 User user1 = WebUtils.request2Bean(request,User.class); user1.setId(WebUtils.makeId()); //----------------------------------- //調用service層的註冊方法,實現註冊 ServiceBussiness serviceBussiness = new UserServiceXML(); serviceBussiness.register(user1); } catch (Exception e) { e.printStackTrace(); }
如今還有問題,若是我填寫信息不合法,提交給服務器驗證之後,服務器應該告訴用戶哪一個信息不合法,而不是直接把跳轉回註冊界面,把全部的信息所有清空,讓用戶從新填寫!
咱們應該這樣作:當發現用戶輸入的信息不合法時,把錯誤的信息記錄下來,等到返回註冊頁面,就提示用戶哪裏出錯了!
在FormBean對象中添加一個HashMap集合(由於等會還要根據關鍵字把錯誤信息顯示給用戶!)
FormBean的所有代碼以下:
//表單提交過來的數據全都是String類型的,birthday也不例外! private String username; private String password; private String password2; private String email; private String birthday; //記錄錯誤的信息 private HashMap<String, String> error = new HashMap<>(); /*用於判斷表單提交過來的數據是否合法*/ public boolean validate() { //用戶名不能爲空,而且要是3-8的字符 abcdABcd if (this.username == null || this.username.trim().equals("")) { error.put("username", "用戶名不能爲空,而且要是3-8的字符"); return false; } else { if (!this.username.matches("[a-zA-Z]{3,8}")) { error.put("username", "用戶名不能爲空,而且要是3-8的字符"); return false; } } //密碼不能爲空,而且要是3-8的數字 if (this.password == null || this.password.trim().equals("")) { error.put("password", "密碼不能爲空,而且要是3-8的數字"); return false; } else { if (!this.password.matches("\\d{3,8}")) { error.put("password", "密碼不能爲空,而且要是3-8的數字"); return false; } } //兩次密碼要一致 if (this.password2 != null && !this.password2.trim().equals("")) { if (!this.password2.equals(this.password)) { error.put("password2", "兩次密碼要一致"); return false; } } //郵箱能夠爲空,若是爲空就必須合法 if (this.email != null && !this.email.trim().equals("")) { if (!this.email.matches("\\w+@\\w+(\\.\\w+)+")) { error.put("email", "郵箱不合法!"); return false; } } //日期能夠爲空,若是爲空就必須合法 if (this.birthday != null && !this.birthday.trim().equals("")) { try { DateLocaleConverter dateLocaleConverter = new DateLocaleConverter(); dateLocaleConverter.convert(this.birthday); } catch (Exception e) { error.put("birthday", "日期不合法!"); return false; } } //若是上面都沒有執行,那麼就是合法的了,返回true return true; } //.....各類的setter和getter
在跳轉到註冊頁面以前,把formbean對象存到request域中。在註冊頁面就能夠把錯誤的信息取出來(使用EL表達式)!
處理表單的Servlet的部分代碼
//驗證表單的數據是否合法,若是不合法就跳轉回去註冊的頁面 if(formBean.validate()==false){ //在跳轉以前,把formbean對象傳遞給註冊頁面 request.setAttribute("formbean", formBean); request.getRequestDispatcher("/WEB-INF/register.jsp").forward(request, response); return; }
作到這裏,仍是有丟丟的問題,咱們不該該把用戶輸入的數據所有清空的!你想一想,若是用戶註冊須要輸入多個信息,僅僅一個出錯了,就把所有信息清空,要他從新填寫,這樣是不合理的!
尚未完善,細心的朋友能夠發現,上面圖的日期也是錯誤的,可是沒一次性標記出來給用戶!要改也十分簡單:在驗證的時候,不要先急着return false 用一個布爾型變量記住,最後返回布爾型的變量便可
不管註冊成功仍是失敗都須要給用戶一個友好界面的!
登錄和註冊是相似的,咱們按着註冊的步驟來寫就對了!
首先寫一個提供登錄界面的Servlet
//直接跳轉到登錄界面 request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
<h1>這是登錄界面</h1> <form action="${pageContext.request.contextPath}/LoginServlet" method="post"> <table> <tr> <td>用戶名</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密碼</td> <td><input type="password" name="password"></td> </tr> <tr> <td><input type="submit" value="提交"></td> <td><input type="reset" name="重置"></td> </tr> </table> </form>
//獲取提交過來的數據 String username = request.getParameter("username"); String password = request.getParameter("password"); //調用service層的方法,去查詢數據庫(XML)是否有該條記錄 try { ServiceBussiness serviceBussiness = new UserServiceXML(); User user = serviceBussiness.login(username, password); if (user == null) { request.setAttribute("message", "用戶名或密碼是錯的"); } else { request.setAttribute("message","登錄成功"); } } catch (Exception e) { e.printStackTrace(); request.setAttribute("message","登錄失敗咯"); } request.getRequestDispatcher("/message.jsp").forward(request, response);
<h1>這是首頁!</h1> <a href="${pageContext.request.contextPath}/LoginUIServlet">登錄</a> <a href="${pageContext.request.contextPath}/RegisterUIServlet">註冊</a> </body>
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章的同窗,能夠關注微信公衆號:Java3y