mvc開發模式簡介
M: Model模型 JavaBean
V:view視圖 JSP
C:Controller控制器 Servlet
其實就是JSP + Servlet + JavaBean
上面的JavaBean就是一個普通類(實體bean),包含三部分:構造方法、私有成員變量、公共的getter和setter方法。
上圖中是一個簡單的MVC模式的流程圖,其中各層的主要作用如下:
M:封裝結果數據集
V:將最終的結果展示給用戶
C:處理業務流程,將數據集發送給JSP
MVC模式的優點:降低各個模塊之間的耦合,能夠增強程序的可維護性和可擴展性,提高了模型的復用性。
MVC模式的缺點:增加了程序源碼的復雜性。
在實際web開發中,通常會使用MVC的開發模式進行編碼。
分層的開發思想
在實際開發中往往采用分層的開發思想,該思想是基於MVC模式的。
分層的思想將不同的模塊內容分開,可以方便軟件開發人員分工協作,提高開發效率,實現了軟件開發的高內聚低耦合。
業務需求分析
在實際開發中,通常會有專門的人去跟客戶進行溝通從而了解客戶需要什么樣的系統,之后由專業的美工將要做的系統以圖片的形式表現出來,客戶確認后作出一些靜態的html demo頁面,然后由軟件開發人員創建相關數據庫,編寫代碼將該靜態頁面做成動態頁面,由測試人員通過測試后將其交付給客戶使用。
這里主要以學習為目的,所以簡化一些流程,通常一般的注冊和登錄都由下面幾個頁面組成:
- 注冊頁面:沒有用戶名時,首先需要在該頁面注冊成功之后才可進行登錄操作,用戶所提交的數據要持久化到數據庫中。
- 登錄頁面:用戶輸入用戶名和密碼提交給后台處理。
- 登錄成功頁面:根據輸入的用戶名和密碼去數據庫中查找匹配的數據,如果存在則跳轉登錄成功頁面,否則提示用戶登錄失敗。
開發前的准備
數據庫的設計,該功能比較簡單,使用一張表就能完成業務邏輯,創建一個名為t_user的表,語句如下:
1 CREATE TABLE 't_user' ( 2 'id' INT NOT NULL AUTO_INCREMENT, 3 'name' VARCHAR(45) NOT NULL, 4 'password' VARCHAR(45) NULL, 5 'email' VARCHAR(45) NULL, 6 'birthday' DATE NULL, 7 PRIMARY KEY ('id'));
搭建web開發環境:
在eclipse中創建一個web項目,因為要使用jdbc,因此將數據庫驅動相關的jar包加入到項目中。
根據分層的開發思想,創建以下包名:
- com.monkey1024.bean
- com.monkey1024.servlet
- com.monkey1024.service
- com.monkey1024.service.impl
- com.monkey1024.dao
- com.monkey1024.dao.impl
- com.monkey1024.util
在src下創建db.properties
1 driverClass=com.mysql.jdbc.Driver 2 url=jdbc:mysql://localhost:3306/m-login 3 username=root 4 password=monkey1024
在com.monkey1024.util包下創建數據庫工具類DBUtil:
1 package com.monkey1024.util; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.SQLException; 6 import java.util.ResourceBundle; 7 8 public class DBUtil { 9 10 private static String driverClass; 11 private static String url; 12 private static String username; 13 private static String password; 14 15 static{ 16 ResourceBundle rb = ResourceBundle.getBundle("db"); 17 driverClass = rb.getString("driverClass"); 18 url = rb.getString("url"); 19 username = rb.getString("username"); 20 password = rb.getString("password"); 21 try { 22 //注冊驅動 23 Class.forName(driverClass); 24 } catch (ClassNotFoundException e) { 25 e.printStackTrace(); 26 } 27 } 28 29 public static Connection getConnection() throws SQLException{ 30 return DriverManager.getConnection(url, username, password); 31 } 32 }
在com.monkey1024.bean包下創建實體類User:
1 package com.monkey1024.bean; 2 3 import java.util.Date; 4 5 /** 6 * 用戶表(t_user) 7 * 8 */ 9 public class User { 10 11 private int id; 12 private String name; 13 private String password; 14 private String email; 15 private Date birthday; 16 17 public int getId() { 18 return id; 19 } 20 public void setId(int id) { 21 this.id = id; 22 } 23 public String getName() { 24 return name; 25 } 26 public void setName(String name) { 27 this.name = name; 28 } 29 public String getPassword() { 30 return password; 31 } 32 public void setPassword(String password) { 33 this.password = password; 34 } 35 public String getEmail() { 36 return email; 37 } 38 public void setEmail(String email) { 39 this.email = email; 40 } 41 public Date getBirthday() { 42 return birthday; 43 } 44 public void setBirthday(Date birthday) { 45 this.birthday = birthday; 46 } 47 }
實現注冊功能
注冊功能需要向數據庫中添加數據,首先在com.monkey1024.dao包下創建接口UserDao:
1 package com.monkey1024.dao; 2 3 import com.monkey1024.bean.User; 4 5 /** 6 * 用戶dao 7 * 8 */ 9 public interface UserDao { 10 11 /** 12 * 添加用戶信息 13 * @param user 14 * @throws Exception 15 */ 16 public void addUser(User user) throws Exception; 17 }
在com.monkey1024.dao.impl包下創建接口UserDao的實現類UserDaoImpl,該類中的addUser方法主要實現向數據庫插入的功能:
1 package com.monkey1024.dao.impl; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.text.SimpleDateFormat; 6 7 import com.monkey1024.bean.User; 8 import com.monkey1024.dao.UserDao; 9 import com.monkey1024.util.DBUtil; 10 11 public class UserDaoImpl implements UserDao { 12 13 @Override 14 public void addUser(User user) throws Exception { 15 Connection conn = null; 16 PreparedStatement ps = null; 17 try { 18 conn = DBUtil.getConnection(); 19 ps = conn.prepareStatement("INSERT INTO t_user(name,password,email,birthday) VALUES(?,?,?,?)"); 20 ps.setString(1, user.getName()); 21 ps.setString(2, user.getPassword()); 22 ps.setString(3, user.getEmail()); 23 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 24 String birthday = sdf.format(user.getBirthday()); 25 ps.setString(4, birthday); 26 ps.executeUpdate(); 27 } catch (Exception e) { 28 e.printStackTrace(); 29 throw new RuntimeException("添加失敗!"); 30 } 31 } 32 33 }
在com.monkey1024.service包下創建UserService接口:
1 package com.monkey1024.service; 2 3 import com.monkey1024.bean.User; 4 5 public interface UserService { 6 7 /** 8 * 添加用戶信息 9 * @param user 10 * @throws Exception 11 */ 12 public void addUser(User user) throws Exception; 13 14 }
在com.monkey1024.service.impl包下創建UserServiceImpl實現類:
1 package com.monkey1024.service.impl; 2 3 import com.monkey1024.bean.User; 4 import com.monkey1024.dao.UserDao; 5 import com.monkey1024.dao.impl.UserDaoImpl; 6 import com.monkey1024.service.UserService; 7 8 public class UserServiceImpl implements UserService { 9 10 UserDao userDao = new UserDaoImpl(); 11 12 @Override 13 public void addUser(User user) throws Exception { 14 userDao.addUser(user); 15 } 16 17 }
在com.monkey1024.servlet包下創建RegistServlet處理請求數據:
1 package com.monkey1024.servlet; 2 3 import java.io.IOException; 4 import java.text.ParseException; 5 import java.text.SimpleDateFormat; 6 import java.util.Date; 7 8 import javax.servlet.ServletException; 9 import javax.servlet.http.HttpServlet; 10 import javax.servlet.http.HttpServletRequest; 11 import javax.servlet.http.HttpServletResponse; 12 13 import com.monkey1024.bean.User; 14 import com.monkey1024.service.UserService; 15 import com.monkey1024.service.impl.UserServiceImpl; 16 17 /** 18 * 用戶注冊 19 */ 20 public class RegistServlet extends HttpServlet { 21 private static final long serialVersionUID = 1L; 22 23 protected void doGet(HttpServletRequest request, HttpServletResponse response) 24 throws ServletException, IOException { 25 request.setCharacterEncoding("UTF-8"); 26 response.setContentType("text/html;charset=UTF-8"); 27 28 //將表單提交的數據放在User類中 29 User u = new User(); 30 u.setName(request.getParameter("name")); 31 u.setPassword(request.getParameter("password")); 32 u.setEmail(request.getParameter("email")); 33 String birthday = request.getParameter("birthday"); 34 try { 35 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 36 Date date = sdf.parse(birthday); 37 u.setBirthday(date); 38 } catch (ParseException e) { 39 e.printStackTrace(); 40 } 41 42 //調用業務邏輯 43 UserService us = new UserServiceImpl(); 44 try { 45 us.addUser(u); 46 // 分發轉向 47 response.getWriter().write("注冊成功!1秒跳轉到主頁"); 48 response.setHeader("refresh", "1;url=" + request.getContextPath() 49 + "/login.jsp"); 50 } catch (Exception e) { 51 e.printStackTrace(); 52 } 53 54 55 } 56 57 protected void doPost(HttpServletRequest request, HttpServletResponse response) 58 throws ServletException, IOException { 59 doGet(request, response); 60 } 61 62 }
登錄功能
在UserDao接口中添加下面方法:
1 /** 2 * 根據用戶姓名和密碼查找用戶 3 * @param user 4 * @return 5 * @throws Exception 6 */ 7 public User findUserByNameAndPassword(User user) throws Exception;
在UserDaoImpl實現類中添加下面方法,根據用戶名和密碼去數據庫中查找響應的記錄
1 @Override 2 public User findUserByNameAndPassword(User user) throws Exception { 3 Connection conn = null; 4 PreparedStatement ps = null; 5 ResultSet rs = null; 6 User u = null; 7 try { 8 conn = DBUtil.getConnection(); 9 ps = conn.prepareStatement("select * from t_user where name=? and password=?"); 10 ps.setString(1, user.getName()); 11 ps.setString(2, user.getPassword()); 12 13 rs = ps.executeQuery(); 14 if(rs.next()){ 15 u = new User(); 16 u.setId(rs.getInt(1)); 17 u.setName(rs.getString(2)); 18 u.setPassword(rs.getString(3)); 19 u.setEmail(rs.getString(4)); 20 u.setBirthday(rs.getDate(5)); 21 } 22 } catch (Exception e) { 23 e.printStackTrace(); 24 } 25 return u; 26 }
在UserService中添加下面方法:
1 /** 2 * 根據用戶姓名和密碼查找用戶 3 * @param user 4 * @return 5 * @throws Exception 6 */ 7 public User findUserByNameAndPassword(User user) throws Exception;
在UserServiceImpl實現類中添加下面方法:
1 @Override 2 public User findUserByNameAndPassword(User user) throws Exception { 3 return userDao.findUserByNameAndPassword(user); 4 }
創建LoginServlet用來接收提交的用戶名和密碼,如果根據該用戶名和密碼可以從數據庫中查詢出相應的數據,則可以登錄成功,否則登錄失敗。
1 package com.monkey1024.servlet; 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 com.monkey1024.bean.User; 11 import com.monkey1024.service.UserService; 12 import com.monkey1024.service.impl.UserServiceImpl; 13 14 /** 15 * 用戶登錄 16 */ 17 public class LoginServlet extends HttpServlet { 18 private static final long serialVersionUID = 1L; 19 20 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 21 request.setCharacterEncoding("UTF-8"); 22 response.setContentType("text/html;charset=UTF-8"); 23 User user = new User(); 24 user.setName(request.getParameter("name")); 25 user.setPassword(request.getParameter("password")); 26 27 UserService us = new UserServiceImpl(); 28 29 try { 30 User u = us.findUserByNameAndPassword(user); 31 32 //分發轉向 33 if(u!=null){ 34 //如果登錄成功,就把user對象放到session對象中 35 request.getSession().setAttribute("user", u); 36 request.getRequestDispatcher("/login_success.jsp").forward(request, response); 37 }else{ 38 request.setAttribute("msg", "用戶名或密碼不正確!"); 39 request.getRequestDispatcher("/login.jsp").forward(request, response); 40 } 41 } catch (Exception e) { 42 e.printStackTrace(); 43 } 44 } 45 46 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 47 doGet(request, response); 48 } 49 50 }
注銷功能
創建LogoutServlet,在里面將session銷毀:
1 package com.monkey1024.servlet; 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 /** 10 * 用戶注銷 11 */ 12 public class LogoutServlet extends HttpServlet { 13 private static final long serialVersionUID = 1L; 14 15 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 16 //使用sessions銷毀 17 request.getSession().invalidate(); 18 //重定向到登錄頁面 19 response.sendRedirect(request.getContextPath()+"/login.jsp"); 20 } 21 22 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 23 doGet(request, response); 24 } 25 26 }
表單賦值的問題
當請求request中攜帶了用戶提交的數據時,需要將這些數據封裝到JavaBean中,像之前寫法需要一一賦值,倘若request攜帶了非常多的表單數據,此時的賦值操作就顯得比較繁瑣了,那有沒有好的解決方法呢?這里可以使用apache的commons-beanutils搞定這個問題。
使用commons-beanutils解決表單賦值的問題。
首先需要下載兩個jar包分別是:
commons-beanutils.jar:http://pan.baidu.com/s/1slzAndb
commons-logging.jar: http://pan.baidu.com/s/1eSNDiQA
下載完成后將其拷貝到項目的lib目錄下。
修改RegistServlet中賦值操作如下:
1 //獲取用戶提交的表單數據,並封裝到User中 2 User u = new User(); 3 //使用commons-beanutils將表單數據封裝到User對象中 4 try { 5 //因為User對象中的brithday是Date類型,所以先注冊一個日期轉換器 6 ConvertUtils.register(new DateLocaleConverter(), Date.class); 7 //將表單數據封裝到User對象中 8 BeanUtils.populate(u, request.getParameterMap()); 9 } catch (IllegalAccessException e1) { 10 e1.printStackTrace(); 11 } catch (InvocationTargetException e1) { 12 e1.printStackTrace(); 13 }
修改LoginServlet中賦值操作如下:
1 //獲取用戶提交的表單數據 2 User user = new User(); 3 try { 4 BeanUtils.populate(user, request.getParameterMap()); 5 } catch (IllegalAccessException e1) { 6 // TODO Auto-generated catch block 7 } catch (InvocationTargetException e1) { 8 e1.printStackTrace(); 9 }
上面的BeanUtils.populate(user, request.getParameterMap())方法會遍歷request.getParameterMap()的key,key與user中的屬性一致的話,會將該屬性賦值,所以要使用該方法的前提就是表單中的name值和JavaBean中的屬性值名稱要一致。
用戶名不能重復的問題
在實際應用當中,用戶名是不能重復的,即要保證用戶名在數據庫中的唯一性,要解決這個問題,需要在用戶注冊時先根據填寫的用戶名去數據庫中查詢,如果查詢出結果的話,就說明該用戶名已經被注冊了。
主要代碼如下,修改RegistServlet
1 //使用apache commons-beanutil解決賦值操作 2 try { 3 //因為User中的birthday是Date類型,所以需要先注冊一個日期轉換器 4 ConvertUtils.register(new DateLocaleConverter(), Date.class); 5 //User類中的屬性名需要跟jsp表單中的name保持一致 6 BeanUtils.populate(u, request.getParameterMap()); 7 } catch (IllegalAccessException e1) { 8 e1.printStackTrace(); 9 } catch (InvocationTargetException e1) { 10 e1.printStackTrace(); 11 } 12 13 //調用業務邏輯 14 UserService us = new UserServiceImpl(); 15 try { 16 //判斷用戶名是否重復 17 User result = us.findUserByName(u); 18 19 //如果不等於null則說明用戶名重復 20 if(result != null){ 21 request.setAttribute("msg", "用戶名重復"); 22 request.getRequestDispatcher("/regist.jsp").forward(request, response); 23 }else{ 24 //用戶名不重復時,執行添加操作 25 us.addUser(u); 26 //分發轉向 27 response.getWriter().write("注冊成功!1秒后跳轉到主頁"); 28 response.setHeader("refresh", "1;url=" + request.getContextPath() + "/login.jsp"); 29 } 30 31 } catch (Exception e) { 32 33 e.printStackTrace(); 34 }
login.jsp
1 <%@ page language="java" contentType="text/html; charset=utf-8" 2 pageEncoding="utf-8"%> 3 <!DOCTYPE html> 4 <html> 5 <head> 6 <meta charset="UTF-8"> 7 <title>首頁</title> 8 </head> 9 <body> 10 ${msg } 11 <form action="${pageContext.request.contextPath }/login" method="post"> 12 <table> 13 <tr> 14 <td>用戶名:</td><td><input type="text" name="name"/></td> 15 </tr> 16 <tr> 17 <td>密 碼:</td><td><input type="password" name="password"/></td> 18 </tr> 19 </table> 20 <input type="submit" value="登錄"/><br/> 21 </form> 22 沒有用戶名?點此<a href="${pageContext.request.contextPath }/regist.jsp">注冊</a> 23 </body> 24 </html>
regist.jsp
1 <%@ page language="java" contentType="text/html; charset=utf-8" 2 pageEncoding="utf-8"%> 3 <!DOCTYPE html> 4 <html> 5 <head> 6 <meta charset="UTF-8"> 7 <title>注冊</title> 8 </head> 9 <body> 10 <form action="${pageContext.request.contextPath }/regist" method="post"> 11 ${msg } 12 <table> 13 <tr> 14 <td>用 戶 名 :</td><td><input type="text" name="name" value=""/></td> 15 </tr> 16 <tr> 17 <td>密 碼 :</td><td><input type="password" name="password" /></td> 18 </tr> 19 <tr> 20 <td>確認密碼:</td><td><input type="password" name="repassword"/></td> 21 </tr> 22 <tr> 23 <td>郵 箱 :</td><td><input type="text" name="email" /></td> 24 </tr> 25 <tr> 26 <!-- 27 <td>生 日 :</td><td><input type="text" name="birthday" /></td> 28 --> 29 <td>生 日 :</td><td><input type="date" name="birthday" /></td> 30 </tr> 31 </table> 32 <input type="submit" value="注冊"/><br/> 33 </form> 34 </body> 35 </html>
login_success.jsp
1 <%@ page language="java" contentType="text/html; charset=utf-8" 2 pageEncoding="utf-8"%> 3 <!DOCTYPE html> 4 <html> 5 <head> 6 <meta charset="UTF-8"> 7 <title>登錄成功</title> 8 </head> 9 <body> 10 歡迎你:${user.name } <br> 11 <a href="${pageContext.request.contextPath }/logout">注銷</a> 12 </body> 13 </html>