1. J2EE - MVC 設計模式初步

一、概念

MVC 把應用程序分成三個部分,即模型、視圖、控制器,每個部分執行自己的任務。
模型是應用程序的主體部分,模型代表業務數據和業務邏輯。一個模型能爲多個視圖提供數據,從而提高了代碼的重用性。
視圖只負責與用戶交互,不進行實際的業務處理。
控制器接收請求並決定調用哪個模型組件去處理請求,然後決定調用哪個視圖來顯示模型返回的數據。

二、MVC 簡單示例

假設現在有一個數據庫,裏面有一張記錄了學生信息的表,我們需要使用 MVC 的設計思想編寫程序將所有信息顯示出來。
學生信息數據表.png
完整的程序應該有這樣的功能:
在首個 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>

最終在瀏覽器顯示的結果如下:
學生信息列表.png

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 加載圖片所用。
json信息.png

在 Android 端解析 json 之後加載數據,可以得到如下的列表,代碼就不詳細介紹了。
Android 端顯示.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章