1. 程式人生 > >javaEE之MVC三層架構及註冊登入案例

javaEE之MVC三層架構及註冊登入案例

一、MVC之三層架構

1、Servlet和JSP(模型1:JSP+javabean)

最佳實踐:Servlet處理邏輯,把結果封裝到域物件中(ServletRequest、HttpSession、ServletContext),轉發給JSP,讓JSP只負責顯示。

資料的封裝要用到JavaBean。架構思路如下:


2、MVC和三層架構圖(模型2)

降低了各層之間的依賴,方便後期擴充套件與維護


3、開發順序:

(1)javabean開始 com.fengyuwuzu.domain

封裝資料,名詞:使用者

(2)業務介面com.fengyuwuzu.service

BusinessService:

void regist(User user) throws UserExistException;

User login(String username,String password);

(3)DAO介面com.fengyuwuzu.dao

void save(User user);

User findUser(String username);

User findUser(String username,String password);

(4)DAO實現com.fengyuwuzu.dao.impl

public class UserDaoMySQLImpl implements UserDao

(5)業務實現com.fengyuwuzu.service.impl

public class BusinessServiceImpl implements BusinessService

(6)單元測試com.fengyuwuzu.test

(7)表現層:com.fengyuwuzu.web.controller

html+jsp+servlet。LoginServlet+LogoutServlet+RegistServlet

(8)工具類com.fengyuwuzu.util

JdbcUtil、FillBeanUtil

二、登入註冊案例

1、建立javabean及資料庫表:

首先我們根據目的,來建立javabean,我們希望實現登入和註冊,那麼現在我們針對是使用者註冊資訊。

public class User implements Serializable {
	private String username;
	private String password;
	private String email;
	private Date birthday;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}。。。

use day15;
create table user(
username varchar(100) primary key,
password varchar(100) not null,
email varchar(100) not null,
birthday date
);

2、業務介面

有了javabean我們操作的物件也就明確了,這個時候我們要明確我們要做什麼,現在我們要註冊和登入,所以在業務邏輯層,我們要實現的是註冊和登入函式。這裡只是根據要設計的功能,寫出介面函式。

//根據需求來定
//介面:把註釋寫的沒有歧義
public interface BusinessService {
	/**
	 * 使用者註冊
	 * @param user 註冊資訊
	 * @throws UserExistException 如果註冊使用者名稱已經存在了,丟擲此異常
	 */
	void regist(User user) throws UserExistException;
	/**
	 * 完成使用者登入
	 * @param username 使用者名稱
	 * @param password 密碼
	 * @return 如果使用者名稱或密碼錯誤,要返回null
	 */
	User login(String username,String password);
}

3、DAO介面

我們明確了業務要實現的幾個功能,而這些功能依賴於持久層的資料,我們要明確持久層的操作,即資料的儲存和查詢:

//不負責業務邏輯,只負責CRUD
public interface UserDao {
	/**
	 * 儲存使用者資訊
	 * @param user
	 */
	void save(User user);
	/**
	 * 根據使用者名稱查詢使用者
	 * @param username
	 * @return 如果不存在返回null
	 */
	User findUser(String username);
	/**
	 * 根據使用者名稱和密碼查詢使用者
	 * @param username
	 * @param password
	 * @return 如果不存在返回null
	 */
	User findUser(String username,String password);
}

4、DAO實現

明確了底層要進行資料的儲存和查詢,那麼現在我們可以實現DAO層的具體實現,這樣我就可以接著實現業務邏輯層的實現了。

DAO的實現就是:連線資料庫,進行資料庫的CRUD

public class UserDaoMySQLImpl implements UserDao {

	public void save(User user) {
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		try{
			conn = JdbcUtil.getConnection();
			stmt = conn.createStatement();
			stmt.executeUpdate("insert into user (username,password,email,birthday) values ('"+user.getUsername()+"','"+user.getPassword()+"','"+user.getEmail()+"','"+user.getBirthday().toLocaleString()+"')");
		}catch(Exception e){
			throw new DaoException(e);
		}finally{
			JdbcUtil.release(rs, stmt, conn);
		}
	}

	public User findUser(String username) {
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		try{
			conn = JdbcUtil.getConnection();
			stmt = conn.createStatement();
			rs = stmt.executeQuery("select * from user where username='"+username+"'");
			if(rs.next()){
				User user = new User();
				user.setUsername(rs.getString("username"));
				user.setEmail(rs.getString("email"));
				user.setBirthday(rs.getDate("birthday"));
				return user;
			}else{
				return null;
			}
		}catch(Exception e){
			throw new DaoException(e);
		}finally{
			JdbcUtil.release(rs, stmt, conn);
		}
	}

	public User findUser(String username, String password) {
		Connection conn = null;
		Statement stmt = null;
		ResultSet rs = null;
		try{
			conn = JdbcUtil.getConnection();
			stmt = conn.createStatement();
			rs = stmt.executeQuery("select * from user where username='"+username+"' and password='"+password+"'");
			if(rs.next()){
				User user = new User();
				user.setUsername(rs.getString("username"));
				user.setEmail(rs.getString("email"));
				user.setBirthday(rs.getDate("birthday"));
				return user;
			}else{
				return null;
			}
		}catch(Exception e){
			throw new DaoException(e);
		}finally{
			JdbcUtil.release(rs, stmt, conn);
		}
	}

}

5、業務實現

我們實現了DAO介面,並具體實現了DAO操作的實現,那我們業務邏輯的實現就可以利用DAO的實現來操作資料庫的內容了。

業務實現,是為servlet服務的,這樣servlet就可以呼叫這裡實現的方法操作到底層的資料了。

public class BusinessServiceImpl implements BusinessService {
	private UserDao dao = BeanFactory.getUserDao();
	public void regist(User user) throws UserExistException {
		if(user==null)
			throw new IllegalArgumentException("The param user can not be null");
		User dbUser = dao.findUser(user.getUsername());
		if(dbUser!=null){
			throw new UserExistException("The username \""+user.getUsername()+"\"使用者已經存在");
		}
		dao.save(user);
	}

	public User login(String username, String password) {
		return dao.findUser(username, password);
	}

}

6、單元測試

之上我們實現了業務邏輯和DAO層的東西,那麼現在我們可以進行單元測試,測試業務邏輯層對資料庫的操作是否都是正常的,一旦測試通過,證明我們業務邏輯都已經完全打通,可以交給表現層呼叫了

7、表現層

使用者點選-->觸發 servlet被呼叫-->servlet裡面建立業務邏輯的例項,來呼叫裡面的方法-->持久層資料被操作。

(1)首頁jsp:

  <body>
    <h1>XX網站</h1>
    <hr/>
    <c:if test="${sessionScope.user==null}">
    	<a href="${pageContext.request.contextPath}/regist.jsp">註冊</a>
    	<a href="${pageContext.request.contextPath}/login.jsp">登入</a>
    </c:if>
    <c:if test="${sessionScope.user!=null}">
    	歡迎您:${sessionScope.user.username}
    	<a href="${pageContext.request.contextPath}/servlet/LogoutServlet">登出</a>
    </c:if>
  </body>

(2)註冊jsp
  <body>
    <form action="${pageContext.request.contextPath}/servlet/RegistServlet" method="post" enctype="application/x-www-form-urlencoded">
    	<!-- 
    	約定優於編碼:遵守好的約定,會使程式碼編寫更加高效
    	目前約定:表單的欄位名和JavaBean的屬性名保持了一致
    	 -->
    	<table border="1" width="738">
    		<tr>
    			<td>使用者名稱:</td>
    			<td>
    				<input type="text" name="username" value="${formBean.username}"/>${formBean.errors.username}
    			</td>
    		</tr>
    		<tr>
    			<td>密碼:</td>
    			<td>
    				<input type="password" name="password" value=""/>${formBean.errors.password}
    			</td>
    		</tr>
    		<tr>
    			<td>確認密碼:</td>
    			<td>
    				<input type="password" name="repassword"/>${formBean.errors.repassword}
    			</td>
    		</tr>
    		<tr>
    			<td>郵箱:</td>
    			<td>
    				<input type="text" name="email" value="${formBean.email}"/>${formBean.errors.email}
    			</td>
    		</tr>
    		<tr>
    			<td>生日yyyy-MM-dd:</td>
    			<td>
    				<input type="text" name="birthday" value="${formBean.birthday}" readonly="readonly" onClick="return showCalendar('birthday', 'yy-mm-dd');"/>${formBean.errors.birthday}
    			</td>
    		</tr>
    		<tr>
    			<td colspan="2">
    				<input type="submit" value="註冊"/>
    			</td>
    		</tr>
    	</table>
    </form>
  </body>
(3)登入jsp
 <body>
    <form action="${pageContext.request.contextPath}/servlet/LoginServlet" method="post" enctype="application/x-www-form-urlencoded">
    	<table border="1" width="438">
    		<tr>
    			<th>使用者名稱:</th>
    			<td>
    				<input type="text" name="username"/>
    			</td>
    		</tr>
    		<tr>
    			<th>密碼:</th>
    			<td>
    				<input type="text" name="password"/>
    			</td>
    		</tr>
    		<tr>
    			<td colspan="2">
    				<input type="submit" value="登入"/>
    			</td>
    		</tr>
    	</table>
    </form>
  </body>
(4)RegistServlet
//編碼重點。完成註冊
public class RegistServlet extends HttpServlet {
	private BusinessService s = new BusinessServiceImpl();
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String encoding = "UTF-8";
		request.setCharacterEncoding(encoding);
		response.setContentType("text/html;charset="+encoding);
		
		//獲取表單資料,封裝到JavaBean中。引入FormBean:特點屬性和表單欄位完全一致。且都是String型別。封裝錯誤訊息。
		UserFormBean formBean = FillBeanUtil.fillBean(request, UserFormBean.class);
		//資料驗證:伺服器端驗證。實際開發中:客戶端+伺服器端驗證。
		if(!formBean.validate()){
			//不通過:回顯資料,訊息提示
			request.setAttribute("formBean", formBean);
			request.getRequestDispatcher("/regist.jsp").forward(request, response);
			return;
		}
		
		//填充模型:formBean---->JavaBean
		User user = new User();
//		user.setUsername(formBean.getUsername());
//		user.setPassword(formBean.getPassword());
//		user.setEmail(formBean.getEmail());
//		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
//		try {
//			user.setBirthday(df.parse(formBean.getBirthday()));
//		} catch (ParseException e) {
//			e.printStackTrace();
//		}
		
		ConvertUtils.register(new DateLocaleConverter(), Date.class);//註冊型別轉換器
		try {
			BeanUtils.copyProperties(user, formBean);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//通過:呼叫Service儲存資料
		try {
			s.regist(user);
			response.getWriter().write("儲存成功!2秒後轉向主頁");
			response.setHeader("Refresh", "2;URL="+request.getContextPath());
		} catch (UserExistException e) {
			//資料回顯和提示
			formBean.getErrors().put("username", "使用者名稱已經存在了");
			request.setAttribute("formBean", formBean);
			request.getRequestDispatcher("/regist.jsp").forward(request, response);
			return;
		}
	}
(5)LoginServlet
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String encoding = "UTF-8";
		request.setCharacterEncoding(encoding);
		response.setContentType("text/html;charset="+encoding);
		
		String username = request.getParameter("username");
		String password = request.getParameter("password");
		
		User user = s.login(username, password);
		if(user==null){
			//錯誤的使用者名稱或密碼
			response.getWriter().write("錯誤的使用者名稱或密碼。2秒後轉向登入頁面。");
			response.setHeader("Refresh", "2;URL="+request.getContextPath()+"/login.jsp");
			return;
		}
		
		//登入成功
		request.getSession().setAttribute("user", user);
		response.getWriter().write("登入成功。2秒後轉向主頁。");
		response.setHeader("Refresh", "2;URL="+request.getContextPath());
		
	}
(6)LogoutServlet
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String encoding = "UTF-8";
		request.setCharacterEncoding(encoding);
		response.setContentType("text/html;charset="+encoding);
		
		request.getSession().removeAttribute("user");
//		request.getSession().invalidate();
		response.getWriter().write("登出成功。2秒後轉向主頁。");
		response.setHeader("Refresh", "2;URL="+request.getContextPath());
	}
8、工具類

為了簡化程式碼,避免重複性的程式碼,將常用的程式碼抽取出來。如資料庫操作,javabean填充等

(1)JdbcUtil

//與具體的資料庫解耦
public class JdbcUtil {

	private static String driverClass;
	private static String url;
	private static String user;
	private static String password;
	
	static{
		try {
			InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("dbcfg.properties");
			Properties props = new Properties();
			props.load(in);
			
			driverClass = props.getProperty("driverClass");
			url = props.getProperty("url");
			user = props.getProperty("user");
			password = props.getProperty("password");
			Class.forName(driverClass);
		} catch (Exception e) {
			throw new ExceptionInInitializerError(e);
		}
		
	}

	public static Connection getConnection() throws Exception {
		Connection conn = DriverManager.getConnection(url,user,password);
		return conn;
	}

	public static void release(ResultSet rs, Statement stmt, Connection conn) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			stmt = null;
		}
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			conn = null;
		}
	}
}

(2)填充javabean

依賴於:

commons-beanutils-1.8.3.jar

commons-logging-1.1.1.jar

public class FillBeanUtil {
	public static <T> T fillBean(HttpServletRequest request,Class<T> clazz){
		try {
			T bean = clazz.newInstance();
			BeanUtils.populate(bean, request.getParameterMap());
			return bean;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

(3)BeanFactory
public class BeanFactory {
	
	private static String userDao;
	static{
		try {
			InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("dao.properties");
			Properties props = new Properties();
			props.load(in);
			userDao = props.getProperty("userDao");
		} catch (IOException e) {
			throw new ExceptionInInitializerError(e);
		}
	}
	
	public static UserDao getUserDao(){
		try {
			return (UserDao) Class.forName(userDao).newInstance();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}


myeclipse工程程式碼下載: