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);
}
}
}