一、概念
MVC 把應用程序分成三個部分,即模型、視圖、控制器,每個部分執行自己的任務。
模型是應用程序的主體部分,模型代表業務數據和業務邏輯。一個模型能爲多個視圖提供數據,從而提高了代碼的重用性。
視圖只負責與用戶交互,不進行實際的業務處理。
控制器接收請求並決定調用哪個模型組件去處理請求,然後決定調用哪個視圖來顯示模型返回的數據。
二、MVC 簡單示例
假設現在有一個數據庫,裏面有一張記錄了學生信息的表,我們需要使用 MVC 的設計思想編寫程序將所有信息顯示出來。
完整的程序應該有這樣的功能:
在首個 jsp 界面點擊 “顯示信息” 的鏈接後,調用 Servlet 的 doGet() 方法,doGet()會調用 DAO(負責操作數據庫的類) 中的方法獲取所有學生的信息,最後顯示在視圖中。
這裏的 Servlet 就是控制器,DAO 就是模型,界面就是視圖。
1. 首頁 jsp
首頁的 jsp 頁面只負責提供一個超鏈接供用戶跳轉到信息顯示頁面,只要在中加入一個超鏈接即可。
<body>
<a href="listAllServlet">List All Students</a>
</body>
我們看到,超鏈接會讓我們跳轉到 listAllServlet 這個 Servlet 程序中,而 Servlet 作爲控制器,又將怎麼連接模型和試圖呢?
2. listAllServlet
在 Eclipse 中新建一個 Servlet,會生成如下所示的代碼。
@WebServlet("/listAllServlet")
public class ListAllServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public ListAllServlet() {
super();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 處理 get 請求
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 處理 post 請求
doGet(request, response);
}
}
Servlet 會在 doGet() 方法中通過 DAO 類獲取到數據庫的信息,在 doGet() 中沒有與數據庫交互的邏輯,它只是調用了 DAO 類的方法,至於 DAO 中是怎麼實現的,Servlet 並不關心。
獲取到數據後,Servlet 將請求轉發給另一個 jsp 頁面來顯示數據(之前查詢到的數據都會放入請求中以便頁面獲取)。
那麼什麼是請求的轉發呢?
請求的轉發就是,客戶首先發送一個請求到服務器端,服務器端發現匹配的servlet,並指定它去執行,當這個 servlet 執行完之後,調用 request.getRequestDispacther() 方法,把請求轉發給指定的 jsp 頁面,整個流程都是在服務器端完成的,而且是在同一個請求裏面完成的,因此 servlet 和 jsp 共享的是同一個 request。
與請求的轉發相類似的還有請求的重定向,在重定向過程中,客戶發送一個請求到服務器,服務器匹配 servlet,這和請求轉發一樣。servlet 處理完之後調用了response.sendRedirect() ,響應行告訴客戶端你必須再發一個請求去訪問 jsp 頁面。這裏兩個請求互不干擾,相互獨立。
也就是說,如果目標的響應頁面不需要從 request 中讀取任何值,則可以使用請求的重定向,還可以防止表單的重複提交。
// 請求轉發
request.getRequestDispatcher("/student.jsp").forward(request, response);
//請求的重定向
response.sendRedirect( "/student_list.jsp");
觀察上面的代碼,我們發現請求轉發和請求的重定向對地址的格式是不一樣的,請求轉發中的 “/” 代表當前 web 應用;請求的重定向中的 “/” 代表當前站點,這裏就是8080端口。
3. StudentDao
我們已經知道數據庫中的信息是怎麼存放的了,現在需要先定義一個 Student 實體類來在內存中存放信息。
public class Student {
private String img_path;
private Integer flow_id;
private Integer type;
private String id_card;
private String student_name;
private Integer grade;
public Student(String img_path, Integer flow_id, Integer type, String id_card, String student_name, Integer grade) {
super();
this.img_path = img_path;
this.flow_id = flow_id;
this.type = type;
this.id_card = id_card;
this.student_name = student_name;
this.grade = grade;
}
public Student() {
super();
}
public String getImg_path() {
return img_path;
}
public void setImg_path(String img_path) {
this.img_path = img_path;
}
public Integer getFlow_id() {
return flow_id;
}
public void setFlow_id(Integer flow_id) {
this.flow_id = flow_id;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public String getId_card() {
return id_card;
}
public void setId_card(String id_card) {
this.id_card = id_card;
}
public String getStudent_name() {
return student_name;
}
public void setStudent_name(String student_name) {
this.student_name = student_name;
}
public Integer getGrade() {
return grade;
}
public void setGrade(Integer grade) {
this.grade = grade;
}
@Override
public String toString() {
return "Student [img_path=" + img_path + ", flow_id=" + flow_id + ", type=" + type + ", id_card=" + id_card
+ ", student_name=" + student_name + ", grade=" + grade + "]";
}
}
之後是與數據庫交互的 DAO 類,這裏的 DAO 類使用了原生的方式連接和操作數據庫。
public class StudentDao {
/**
* 獲取所有的學生信息
* @return 學生列表
*/
public List<Student> getAllStudent() {
List<Student> list = new ArrayList<>();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
String driverClass = "com.mysql.jdbc.Driver"; // 驅動程序名
String url = "jdbc:mysql:///student"; // url 指向要訪問的數據庫名
String user = "root";
String password = "root";
// 加載驅動程序
Class.forName(driverClass);
// 獲取連接
connection = DriverManager.getConnection(url, user, password);
String sql = "select img_path, flow_id, type, id_card, student_name, grade "
+ "from examstudent";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
// 遍歷結果集
while(resultSet.next()) {
String img_path = resultSet.getString(1);
int flow_id = resultSet.getInt(2);
int type = resultSet.getInt(3);
String id_card = resultSet.getString(4);
String student_name = resultSet.getString(5);
int grade = resultSet.getInt(6);
// 將數據庫中的每條信息添加到 List<Student> 中
Student student = new Student(img_path, flow_id, type, id_card, student_name, grade);
list.add(student);
}
} catch (Exception e) {
e.printStackTrace();
}
/**
* 將所有的連接關閉
*/
finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return list;
}
}
4. 轉發請求到 jsp 前端
在有了 DAO 類之後,我們就能在 Servlet 中與數據庫交互了。
改寫 doGet() 方法:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
StudentDao dao = new StudentDao();
List<Student> list = dao.getAllStudent();
/**
* 如果前端是 jsp 頁面
* 將請求轉發到 student.jsp 頁面進行顯示
*/
request.setAttribute("students", list); // 將信息保存在請求中
request.getRequestDispatcher("/students.jsp").forward(request, response);
}
從代碼中可以發現,我們將從數據獲取的 list 數據放入了請求中,再轉發給了 students.jsp,之後我們就可以在 jsp 中將學生信息通過列表的方式顯示出來。
students.jsp 代碼如下,我們先從 Servlet 轉發過來的請求中取出我們需要的數據,然後對數據進行遍歷,使用表格的形式將其打印出來。
<%@page import="com.lister.entity.Student"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<% List<Student> students = (List<Student>)request.getAttribute("students"); %>
<table border = "1" cellpadding = "10" cellspacing = "0">
<tr>
<td>flow_id</td>
<td>type</td>
<td>name</td>
<td>id_card</td>
<td>grade</td>
</tr>
<% for (Student student : students) { %>
<tr>
<td><%=student.getFlow_id() %></td>
<td><%=student.getType() %></td>
<td><%=student.getStudent_name() %></td>
<td><%=student.getId_card() %></td>
<td><%=student.getGrade() %></td>
</tr>
<% } %>
</table>
</body>
</html>
最終在瀏覽器顯示的結果如下:
5. 服務器與手機端通信的情況
如果要將信息顯示在手機端,那麼此時手機端就將充當前端的角色。目前服務器與手機端通信文本一般使用 json 格式,這裏我們也使用 json 與Android 手機端通信。
首先導入 Gson jar 包,再次改寫 doGet() 方法,使用 Gson 的方法將 List 轉化成 json 字符串並打印出來。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
StudentDao dao = new StudentDao();
List<Student> list = dao.getAllStudent();
/**
* 如果前端是 jsp 頁面
* 將請求轉發到 student.jsp 頁面進行顯示
*/
// request.setAttribute("students", list);
// request.getRequestDispatcher("/students.jsp").forward(request, response);
/**
* 如果前端是手機端
* 使用 response.getWriter() 將信息打印出來
*/
PrintWriter out = response.getWriter();
Gson gson = new Gson();
String json = gson.toJson(list);
out.print(json);
}
這時如果在瀏覽器中請求數據,得到的會是 json 字符串,如下所示:
每個 json 對象的第一個子段爲圖像的網址,供 Android 加載圖片所用。
在 Android 端解析 json 之後加載數據,可以得到如下的列表,代碼就不詳細介紹了。