不使用什么MVC的案例分析:
利用Servlet與jsp實現登陸請求,數據庫查詢,以及頁面的跳轉邏輯
具體流程如下:
不做任何結構上的考慮,可以簡單的做如下實現:
目錄結構
LoginServlet
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.*;
@WebServlet("/Login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);//轉發到get 執行相同邏輯
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//獲取參數
String name = req.getParameter("username");
String password = req.getParameter("pwd");
String error = null; //錯誤信息
boolean flag = false; //是否登陸成功
//驗證數據可靠性
if(name == null || password == null || name.equals("") || password.equals("")){
error="用戶名和密碼不能為空!";
}else {
//查詢數據庫
try {
Class.forName("com.mysql.jdbc.Driver");
Connection connect = DriverManager.getConnection("jdbc:mysql://127.0.0.1/db1?charsetEncoding=utf-8&user=root&password=admin");
PreparedStatement statement = connect.prepareStatement("select *from user where name = ? and password = ?");
statement.setObject(1,name);
statement.setObject(2,password);
ResultSet set = statement.executeQuery();
if(set.next()){
flag = true;
}else{
error="用戶名和密碼錯誤!";
}
} catch (ClassNotFoundException | SQLException e1) {
error = "服務器忙,請稍后再試!";
}
}
if(flag){
//刪除已存在的錯誤信息,避免后續登錄頁面展示重復展示
req.getSession().removeAttribute("error");
//添加用戶名稱到session
req.getSession().setAttribute("user",name);
//跳轉頁面
req.getRequestDispatcher("./index.jsp").forward(req,resp);
}else{
//添加錯誤信息到session
req.getSession().setAttribute("error",error);
//跳轉頁面
req.getRequestDispatcher("./login.jsp").forward(req,resp);
}
}
}
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<span style="color: red">${error}</span>
<form action="./Login" method="post">
用戶名:<input name="username"/>
<br>
密碼:<input name="pwd">
<br>
<input type="submit" value="登錄">
</form>
</body>
</html>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主頁</title>
</head>
<body>
<h1>你好${user},歡迎登錄</h1>
</body>
</html>
增加需求
通過簡單的邏輯判斷和JDBC就實現了上述需求,接下來我們繼續增加需求
假設現在要增加注冊功能,可以這樣:
- 創建register.jsp
- 創建 RegisterServlet
- 在RegisterServlet,獲取參數,鏈接數據庫,處理注冊邏輯
思考,這樣寫有什么問題?
- 按照這樣的思路,一個功能就需要一個Servlet,而Servlet的生命周期是非常長的,將會大量消耗內存
- 如果不創建其他Servlet,那只能將所有URL交個同一個Servlet處理,這樣的話就不可避免的需要對請求路勁進行判斷,以確定客戶端要執行的功能
- 每個功能都要重復的進行數據庫的相關操作,例如連接數據庫,提取結果等,出現了大量重復代碼
上述列出的問題,在程序開發中是經常遇到的,所以我們不得不對程序的整體結構進行重新思考和設計
MVC
MVC是一種軟件構架模式,所謂模式,就是前輩們給我們總結出的針對固定類型問題的已有解決方案
,這寫方案是經過大量時間,以及大量項目實踐,最終總結出的良好的方案;給開發者,提供了非常好的指導思想,在漫長的技術發展過程中,開發者們總結出了,很多好的模式,如<<23中常見模式>>
一書中講到;
注意:嚴格來說MVC不屬於設計模式,而是構架模式,因為MVC是為程序划分層了次結構,使得程序結構更規范,更合理,降低了耦合度
針對web項目,MVC就是非常通用的構架模式,到底如何去設計一款MVC的程序呢?
MVC分層
為了降低各個功能間的耦合度,提高代碼的可維護性,MVC將程序分為了三個層次
-
M:Model 模型,用於作為數據的載體的Bean,通常不包含復雜邏輯,一個Bean對應數據庫一條記錄
-
V:View 視圖,用於展示Model中的數據,和處理用戶交互
-
C :Controller 控制器,接受View的輸入,根據需要調用Mode,獲取數據后再交給View層進行展示
補充POJO和Bean的區別,POJO更加簡單只要求由set/get,而Bean為了滿足可重用性,有更多的規范要求
圖示:
注意:
View與Model層之間不允許直接交互,必須由Controller來調度
Service層
按照這樣的層次結構,將servlet用於Controller,在增加一個Bean類,來裝載數據,看起來不錯,但是沒有解決根本的問題,業務邏輯和數據庫操作依然擠在Servlet中,必須在對其進行拆分,於是在MVC基礎結構上增加Service
層,用於處理業務邏輯,你也可以叫它業務邏輯層(business)
看起來就像下面這樣:
你可能會問,Service屬於M還是C,它既不是C也不是M,是我們在MVC基礎上擴展出來的,我們經常會在已有模式上擴展新的內容這很正常
使用MVC
現在對之前的程序進行改造
- 增加JavaBean,其中的屬性與對應的表相同,擁有ID,name,password
- 增加service包,並創建UserService類,用作業務邏輯層
- 在控制器Servlet中,調用Service來完成業務功能
- 根據Service的返回結果來決定頁面的跳轉地址
項目結構:
UserLoginServlet:
package com.xx.controller;
import com.xx.exceptions.LoginException;
import com.xx.models.UserBean;
import com.xx.services.UserService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;
@WebServlet("/Login")
public class UserLoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//獲取參數
String name = request.getParameter("username");
String pwd = request.getParameter("pwd");
UserBean reqBean = new UserBean(name,pwd);
//實例化業務邏輯對象
UserService service = new UserService();
try {
//調用對應方法 根據結構跳轉頁面
UserBean resBean = service.CheckLogin(reqBean);
request.getSession().setAttribute("user",resBean.getName());
response.sendRedirect(request.getServletContext().getContextPath() +"/index.jsp");
} catch (LoginException e) {
request.getSession().setAttribute("error",e.getMessage());
response.sendRedirect(request.getServletContext().getContextPath() +"/login.jsp");
}
}
}
UserService:
package com.xx.services;
import com.xx.exceptions.LoginException;
import com.xx.models.UserBean;
import java.sql.*;
public class UserService {
public UserBean CheckLogin(UserBean userBean) throws LoginException {
//驗證參數有效性
if (userBean.getPassword() == null || userBean.getPassword() == null ||
userBean.getPassword().equals("") || userBean.getName().equals("")){
throw new LoginException("用戶名和密碼不能為空");
}
UserBean bean = null;
//查詢數據庫
try {
Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/db1?characterEncoding=utf8&user=root&password=admin");
PreparedStatement preparedStatement = connection.prepareStatement("select *from user where name = ? and password = ?");
preparedStatement.setObject(1, userBean.getName());
preparedStatement.setObject(2, userBean.getPassword());
ResultSet set = preparedStatement.executeQuery();
if (set.next()) {
bean = new UserBean();
bean.setId(set.getInt(1));
bean.setName(set.getString(2));
bean.setPassword(set.getString(3));
}else {
throw new LoginException("用戶名或密碼錯誤!");
}
}catch (SQLException | ClassNotFoundException e){
e.printStackTrace();
throw new LoginException("服務器忙,(數據庫炸了)",e);
}
return bean;
}
}
UserBean僅包含構造器,和setget方法
LoginException,也只是簡單的繼承了Exception
小結:
MVC是Web應用常用的設計模式,其增強了代碼的擴展性,可維護性,降低了各組件的耦合度
在上述案例中通過MVC分層,代碼結構得到了優化,但是細心的你可能已經發現了,
- 業務邏輯層Service中夾雜着與數據庫相關的所有代碼,導致代碼冗余.重復代碼問題依然沒有得到解決
- UserLoginServlet依然只能處理用戶的登錄請求