tomcat伺服器啟動的方式Servlet入門

哦?發表於2020-11-25

程式架構

  • B/S架構
    在這裡插入圖片描述

  • URL:主機地址是當前電腦IP和伺服器埠號、目標資源:訪問的專案名
    在這裡插入圖片描述

Web伺服器

應用伺服器如何得到使用者在瀏覽器傳送的請求呢? ----Web伺服器。
Tomcat伺服器的優點:Apache的開源專案,輕量級,穩定,資源佔用小
在這裡插入圖片描述
修改tomcat埠號:在conf下的server.xml中,編輯
在這裡插入圖片描述

JSP / Servlet 專案

JSP(Java Server Pages)

  • 執行在伺服器端的Java頁面,使用HTML巢狀Java程式碼實現

Servlet(Server Applet)是JAVA Servlet 的簡稱,是使用java語言編寫的執行在伺服器端的程式,具有獨立的平臺和協議特性,用於處理客戶端傳來的HTTP請求,並返回一個響應,能處理doGet()和doPost()等。主要功能在於互動的瀏覽和生成資料,生成動態的web內容

  • 通常來說:servlet是指所有實現類Servlet介面的類
    Servlet由Servlet容器提供,Servlet容器是指提供了Servlet功能的伺服器(如:Tomcat)
    Servelt容器會將Servlet動態的載入到伺服器上,然後通過HTTO請求和HTTP應用於客戶端進行互動

Servlet的技術特點

			1、方便				
			​	Servelt提供了大量的實用工具,比如處理很難完成的HTML表單資料、讀取、設定HTPP的頭,以及處理Cookie和跟蹤會話等				
			2、跨平臺
			
			​	Servlet實用Java類編寫,可以在不同的作業系統平臺和不同的應用程式伺服器平臺執行
			
			3、靈活性和可擴充套件性強
			
			​	採用Servlet開發的web應用程式,由於java類繼承性以及建構函式等特點,使得應用靈活,可隨意擴充套件

servlet執行的體系結構: 瀏覽器傳送請求給HTTP伺服器,HTTP伺服器傳送給Servlet容器(tomcat),Servlet容器再請求到當前的Servlet對應的方法上面。
在這裡插入圖片描述

tomcat的部署與測試

  • 建立一個專案
    在這裡插入圖片描述

  • 將解壓好的tomcat新增到專案中
    在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

  • 在tomcat中建立一個userServlerlet
    在這裡插入圖片描述

  • 由於有介面無法識別,需要新增依賴jar包,將tomcat資料夾下的兩個jar包新增到專案中就可以識別了

  • 先在web-INF建立一個lib目錄,然後點選project Strcture新增tomcat資料夾下的兩個jar包
    在這裡插入圖片描述

在這裡插入圖片描述

第一個servlet登入小專案

JSP作為servlet處理

<%%> 指令碼片段:用於定義區域性的變數、表示式、java程式碼
<%!%>宣告語句用於變數宣告 和 方法的定義
<%=%> 表示式 用於存放變數或者比倒是
<%@ %>指令 通過書信定義jsp的特性,實現與jsp容器的通訊
language :用於指定jsp頁面使用的指令碼語言
import: 用於引用指令碼語言中使用的類檔案
contentType:用於指定頁面MIME型別,以及字元編碼集

通過在JSP上寫java程式碼,進行客戶端與服務端的互動。

  • 首先在login.jsp中寫一個使用者輸入的表單,使用者輸入的使用者名稱和密碼就會通過post請求提交到index.jsp的頁面中,web.xml中設定預設登入頁面為login.jsp,開啟tomcat開啟瀏覽器輸入urlhttp://localhost:8080/javaServlet/ /javaServlet為訪問的專案名 (url在edit configration中檢視 ),就可以預設進去login的頁面

在這裡插入圖片描述

web.xml中設定預設登入頁面為login.jsp
在這裡插入圖片描述

  • 在index.jsp中獲取使用者輸入的內容 request..getParameter 並顯示出來out

  • 使用者輸入的內容後點選提交時會包含request,通過request就可以獲取使用者提交的資訊了, 注意此處獲取使用者輸入的內容是通過input標籤中的name屬性來獲取的,使用者輸入的資訊也是通過name屬性一一對應,request也是通過name屬性獲取使用者輸入對應的資訊。
    在這裡插入圖片描述

  • 測試,當使用者點選提交的時候,頁面就會跳轉到index.jsp中,然後顯示使用者名稱和密碼

使用servlet處理使用者互動

為了讓web知道你寫了servlet,需要在web.xml中設定,web.xml是核心頁面,用於設定當前整個專案。
url-pattern用於 <!--指定瀏覽器通過該url 找到servlet 。一定要加 " / " -->
在這裡插入圖片描述

在login.jsp頁面中將使用者表單提交的地址輸入為 在web.xml設定我們建立的Userservlet被訪問的名稱地址/UserServlet,但是不能加入斜槓,login.jsp不能寫action="/UserServlet",如果這樣寫,當使用者點選提交時就會不會訪問專案下的UserServlet,而直接是/UerServlet,就會找不到。

  • 我們按住ctrl點選UserServlet時,就會定位到web.xml中我們設定的servlet-mapping對映中,通過servlet-mapping找到專案中的Userservlet,按照使用者提交表單時表單設定的get或者post請求來執行相對應的方法

在這裡插入圖片描述

  • demo.UserServlet中的post方法寫相對應要處理的邏輯:
  • 首先設定字元編碼集,防止中文亂碼現象
  • 獲取使用者在表單提交的內容, request..getParameter
    如果在Dopost方法下輸入System.out.println("這是DoPost方法");,當tomcat執行時,使用者輸入玩資訊提交後,後臺就會顯示這句話。
    在這裡插入圖片描述

servlet連線mysql資料庫實現學生登入

三層架構:

dao(資料訪問層):與資料庫交流 執行 查詢 、新增、刪除、修改
service(業務層):呼叫dao層 處理業務或者資料
servlet : 被請求、響應

  • 首先匯入mysql jar包,放入web-INf下的lib中,並對這個jar包新增依賴
  • 建立實體層 entity :建立學生類,用於產生學生物件
  • 建立工具類utils:放入連線Mysql資料庫的工具類BaseDao
  • 建立Dao層:Dao層的作用是連線mysql資料庫的資料訪問層。 首先建立一個學生的介面studentDao,由於要實現學生登入的功能,StudentDao這個介面是定義一個查詢學生的方法,建立繼承StudentDao介面的實現類StudentDaoImpl去實現如何查詢學生的具體邏輯,StudentDaoImpl要繼承連線Mysql的工具類BaseDao,才能根據使用者輸入的學生編號,查詢資料庫中的學生資訊,如果查到的話就將資料儲存到Student中,並返回給StudentDao。此處整個Dao 層的邏輯是根據使用者的資訊找到這個學生物件並返回給業務層,去進行登入邏輯操作。

實現類StudentDaoImpl程式碼如下:

public class StudentDaoImpl extends BaseDao implements studentDao {
    //根據使用者輸入的學生編號,查詢資料庫中的學生資訊,如果查到的話就將資料儲存到Student中,並返回
    @Override
    public Student queryStudent(String stuCode) {

        String sql = "select * from student where studentNo = ?";
        Connection connection = getConnection();
        ResultSet rs = executeQuery(connection, sql, new Object[]{stuCode});
        Student student =null;
        try {
            while (rs.next()) {
                student = new Student(
                        rs.getInt("studentNo"),
                        rs.getString("loginPwd"),
                        rs.getString("studentName"),
                        rs.getString("sex"),
                        rs.getInt("gradeId"),
                        rs.getString("phone"),
                        rs.getString("address"),
                        rs.getString("bornDate"),
                        rs.getString("email"),
                        rs.getString("identityCard")
                );
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            closeAll(connection,null,rs);
        }
        return student;
    }
}
  • 建立業務層service:業務層用於將Dao層資料庫查詢的結果進行下一步邏輯操作。建立一個介面StudentService,定義方法用於查詢學生登入並返回學生物件;建立業務層的實現類StudentServiceImpl,實現登入邏輯的判斷。實現類StudentServiceImpl中例項化Dao層的實現類StudentDaoImpl,並用介面StudentDao來接收,StudentDao呼叫查詢方法查詢學生物件返回給業務層中的實現類StudentServiceImpl,StudentServiceImpl再判斷這個學生物件是否為空,不為空就再次判斷使用者輸入的密碼和資料庫查詢的密碼是否一致,一致的話就返回這個學生物件給servlet。

實現類StudentServiceImpl程式碼如下:

    public Student studentLogin(String stuCode, String stuPwd) {
        //呼叫dao層的查詢方法 通過賬號查詢到學生物件 1、null 賬號錯誤 2、找到了學生物件
        Student student = sDao.queryStudent(stuCode);
       if (null!=student){
           if (student.getLoginPwd().equals(stuPwd)){
               return student;
           }
       }
        return null;
    }

  • servlet中的post方法呼叫業務層的實現類StudentServiceImpl ,要用業務層介面來接收。StudentService studentService= new StudentServiceImpl();,來獲取是否登陸成功的學生物件。

如果學生物件不為null,說明登陸成功,那麼這個使用者就可以訪問其他頁面,因此需要將值儲存在session會話當中,session是共享的,因此其他頁面也可以獲取session中的值,然後重定向到其他頁面index.jsp即可。如果學生物件為null,說明登陸失敗,需要顯示登陸失敗的資訊,因此為帶值跳轉,先將登陸失敗的資訊放入request中,再將request物件一起跳轉到登陸login頁面。
此處理解:session儲存了值,不帶引數的重定向為什麼頁面還可以獲取到值呢?–因為session是由伺服器建立,所以可以直接獲取到session儲存的值。 而請求轉發頁面中是request帶著值跳轉到其他頁面,因此在一次的請求轉發中頁面是可以獲取到request儲存的值。

servlet中doPost程式碼如下:

    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        System.out.println("這是DoPost方法");//後臺輸出
        /*1、在http協議傳輸中只能用ISO-8859-1  會產生中文亂碼*/
        /*設定request字元編碼集合*/
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        //設定傳過來的內容可能是文字text或者html
        response.setContentType("text/html;charset=utf-8");

        //獲取使用者輸入的內容
        String userCode = request.getParameter("userCode");
        String userPwd = request.getParameter("userPwd");

//通過業務層呼叫方法 查詢學生(賬號 密碼)資訊 返回一個學生物件
        Student student = studentService.studentLogin(userCode, userPwd);
if (null!=student){
    // 登陸成功
    //跳轉到index.jsp中
    //兩種方式 跳轉頁面 1、請求轉發  2、重定向
    //如果需要帶有值 跳轉到頁面 請求轉發
    //如果不需要帶有值 跳轉頁面 重定向

    //將使用者資訊存入session 內建物件之一  伺服器可以為每個使用者建立一個會話物件(session)
    //1、session是內建物件不需要你建立  2、由伺服器建立
    //獲取session
    HttpSession session = request.getSession();
    // 獲取session ID
    String sessionId = session.getId();
    //設定session 生命週期 單位秒
    session.setMaxInactiveInterval(30*60);
    //將學生物件儲存在session中
    session.setAttribute("USER",student);
    response.sendRedirect("index.jsp");


}else {
   //登陸失敗
    //重定向到登陸頁面
    request.setAttribute("mag","登入失敗,賬號或者密碼錯誤");
    request.getRequestDispatcher("login.jsp").forward(request,response);
}

    }

  • index.jsp頁面中登入失敗需要獲取servlet裡面request帶值轉發到此頁面中的引數來顯示登入失敗,而request中獲取引數返回的是一個Object型別,我們在servlet中給request新增的引數是String型別,因此需要拆箱操作。

index.jsp

  <%
      //獲取servlet傳遞過來的值
      Student student = (Student) session.getAttribute("USER");
      out.print(student);
  %>
  • login.jsp 中首先接收servlet返回的登入失敗的資訊,此處需要做判斷,如果使用者登入成功或者第一次登入那麼servlet中request通過鍵msg獲取的值就為null,因此不需要顯示登陸失敗的資訊,之後再顯示輸入使用者名稱和密碼,提交按鈕。
 <%
      <%--寫一個form表單,使用者在瀏覽器提交post提交表單時會定位到UserServlet處--%>
<%--此處不能寫action="/UserServlet",如果這樣寫,當使用者點選提交時就會不會訪問專案下的UserServlet,而直接是/UerServlet,就會找不到--%>
<form method="post" action="UserServlet"></form>
<%= request.getAttribute("msg")==null?"":request.getAttribute("msg")%>
賬號:<input type="text" name="userCode" />
密碼:<input type="password" name="userPwd" />
<input type="submit"/> <input type="reset"/>
  %>

同一個servlet下增加登出功能

為了讓servlet可以處理多個請求,所以在Dopost方法中呼叫不同的子方法,每一個方法處理一個請求,比如學生登陸、登出、查詢學生資訊…,為了區分使用者提交的請求,前端頁面在獲取使用者請求時給servlet傳入相應的引數,每一個引數對應一個處理使用者不同請求的方法即可。

用於登出的解決方式就是讓session失效。登出後就跳轉到登陸頁面

UserServlet:

package servlet;

import entity.Student;
import org.apache.catalina.Session;
import service.StudentService;
import service.impl.StudentServiceImpl;

import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import java.io.IOException;

public class UserServlet extends javax.servlet.http.HttpServlet {
    @Override
    public void init() throws ServletException {
        //初始化會先執行,再執行doPost或者Doget方法
        super.init();
    }
    private  StudentService studentService= new StudentServiceImpl();

    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        System.out.println("這是DoPost方法");//後臺輸出
        /*1、在http協議傳輸中只能用ISO-8859-1  會產生中文亂碼*/
        /*設定request字元編碼集合*/
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        //設定傳過來的內容可能是文字text或者html
        response.setContentType("text/html;charset=utf-8");

        switch (request.getParameter("index")){
            case "1":
                login(request,response);
                break;
            case "2":
                logout(request,response);
                break;
            case "3":
                login(request,response);
                break;

            default:
                break;
        }




    }

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
doPost(request,response);
    }

    @Override
    public void destroy() {
        //銷燬方法必須手動呼叫,用於資源釋放,例如資料庫的關閉
        super.destroy();
    }

    /**
     * 為了讓servlet處理多個請求 所以建立不同的方法
     * @param request
     * @param response
     */

    private void  login(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException {
        //獲取使用者輸入的內容
        String userCode = request.getParameter("userCode");
        String userPwd = request.getParameter("userPwd");

//通過業務層呼叫方法 查詢學生(賬號 密碼)資訊 返回一個學生物件
        Student student = studentService.studentLogin(userCode, userPwd);
        if (null!=student){
            // 登陸成功
            //跳轉到index.jsp中
            //兩種方式 跳轉頁面 1、請求轉發  2、重定向
            //如果需要帶有值 跳轉到頁面 請求轉發
            //如果不需要帶有值 跳轉頁面 重定向

            //將使用者資訊存入session 內建物件之一  伺服器可以為每個使用者建立一個會話物件(session)
            //1、session是內建物件不需要你建立  2、由伺服器建立
            //獲取session
            HttpSession session = request.getSession();
            // 獲取session ID
            String sessionId = session.getId();
            //設定session 生命週期 單位秒
            session.setMaxInactiveInterval(30*60);
            //將學生物件儲存在session中
            session.setAttribute("USER",student);
            response.sendRedirect("index.jsp");


        }else {
            //登陸失敗
            //重定向到登陸頁面
            request.setAttribute("mag","登入失敗,賬號或者密碼錯誤");
            request.getRequestDispatcher("login.jsp").forward(request,response);
        }

    }
    /**
     * 用於登出  讓session失效
     * @param request
     * @param response
     * @throws IOException
     * @throws ServletException
     */
    private void logout(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws IOException {
        HttpSession session = request.getSession();
      Student student=(Student)  session.getAttribute("USER");
      session.invalidate();//設定session失效
        response.sendRedirect("login.jsp");

    }
}

login頁面:

<form method="post" action="UserServlet?index=1"></form>
<%= request.getAttribute("msg")==null?"":request.getAttribute("msg")%>
賬號:<input type="text" name="userCode" />
密碼:<input type="password" name="userPwd" />
<input type="submit"/> <input type="reset"/>
</body>
</html>

index頁面:

 <%
      //獲取servlet傳遞過來的值
      Student student = (Student) session.getAttribute("USER");
      if (null==student){response.sendRedirect("login.jsp");}//如果學生物件為空就跳轉到登入頁面
      else ;
  %>
<%--  通過表示式顯示內容,UserServlet?index=2表示向UserServlet傳入引數index為2,UserServlet通過request獲取頁面傳來的引數index--%>
  <%= student.getStudentName()%> <a href="UserServlet?index=2" >登出</a>

理解: 資料訪問層Dao層只用於去資料庫查詢資訊獲取值,業務層來控制具體的業務層

同一個servlet下增加學生資訊查詢功能

大體思路:

按照學生編號或者年級或者姓名查詢到學生並展示到index.jsp中:請求servlet返回request,request儲存一個學生集合,通過指令碼jsp和java程式碼塊來展示

小技巧:

  1. 如果需要迴圈遍歷值並顯示到頁面上,把一段for迴圈的java塊拆分開,包裹html程式碼
  2. 按照學生編號或者年級或者姓名查詢到學生的sql語句的書寫問題:此條件表示任意去除一個條件,都可以查詢到相關結果,而sql語句需要用AND拼接條件,去掉任意一個條件,都會多出一個AND,因此多加一個等值條件,這樣去掉任意一個條件都不會出錯。
  3. sql程式碼如下:
SELECT *
FROM student
JOIN grade
on grade.gradeID= student.gradeId
WHERE 1=1
AND studentNo=? 
AND studentName like ?
AND grade.gradeID=?
  • Dao層:StudentDao中編寫查詢學生資訊的方法,返回一個集合list, StudentDaoImpl類中實現這個方法,由於BaseDao中的查詢語句必須要傳入引數,但是按照目前問題的要求,引數值的大小和個數都是不一定的,因此,我們重寫BaseDao的executeQuery方法,並設定不傳入需要給sql語句遍歷的引數,直接執行傳進來的sql語句

重寫的BaseDao的executeQuery方法如下:


    public ResultSet executeQuery(Connection connection,String sql) {
        try {
            prepareStatement = connection.prepareStatement(sql);
            System.out.println(prepareStatement);
            resultSet = prepareStatement.executeQuery();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return resultSet;
    }

StudentDao檔案中

    List<Student> queryAll(String stuNo,String stuName,String gradeId);

StudentDaoImpl類中實現這個方法:

     public List<Student> queryAll(String stuNo, String stuName, String gradeId) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("SELECT * ");
        stringBuffer.append("        FROM student ");
        stringBuffer.append("JOIN grade ");
        stringBuffer.append("on grade.gradeID= student.gradeId ");
        stringBuffer.append("WHERE 1=1 ");
        if (stuNo!=null&&!stuNo.equals(""))
        stringBuffer.append("AND studentNo= "+stuNo );
        if (stuName!=null&&!stuName.equals(""))
        stringBuffer.append("AND studentName like  '%"+stuName+"%'  " );
        if (gradeId!=null&&!gradeId.equals(""))
        stringBuffer.append("        AND grade.gradeID= "+gradeId );

        Connection connection = getConnection();

        ResultSet rs = executeQuery(connection, stringBuffer.toString());
        ArrayList<Student> studentList = new ArrayList<>();
        try {
            while (rs.next()){
                studentList.add( new Student(
 //學生物件屬性根據資料庫中student表中的設計表屬性順序新增,因此通過資料庫獲取的學生資訊也跟Student類的屬性順序一致,
                                rs.getInt("studentNo"),
                                rs.getString("loginPwd"),
                                rs.getString("studentName"),
                                rs.getString("sex"),
                                rs.getInt("gradeId"),
                                rs.getString("phone"),
                                rs.getString("address"),
                                rs.getString("bornDate"),
                                rs.getString("email"),
                                rs.getString("identityCard"),
                                rs.getString("gradeName")
                        )

                );
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            closeAll(connection,null,rs);
        }


        return studentList;
    }
  • 業務層 介面定義方法名,實現類用於實現
    studentservice檔案:
List<Student> queryStudent(String stuNo,String stuName,String gradeId);

studentserviceImpl檔案:

  public List<Student> queryStudent(String stuNo, String stuName, String gradeId) {
        List<Student> students = sDao.queryAll(stuNo, stuName, gradeId);
        return students;
    }
  • UserServlet層:寫一個查詢到學生資訊的方法,並將資訊儲存到request中,請求轉發到index.jsp中。此方法用在使用者輸入完登入資訊後也就是登入方法後,就會顯示查詢到使用者的相關資訊。而無需再使用者登入後就跳轉頁面。

    private void login(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException {
        //獲取使用者輸入的內容
        String userCode = request.getParameter("userCode");
        String userPwd = request.getParameter("userPwd");

//通過業務層呼叫方法 查詢學生(賬號 密碼)資訊 返回一個學生物件
        Student student = studentService.studentLogin(userCode, userPwd);
        if (null != student) {
            // 登陸成功
            //跳轉到index.jsp中
            //兩種方式 跳轉頁面 1、請求轉發  2、重定向
            //如果需要帶有值 跳轉到頁面 請求轉發
            //如果不需要帶有值 跳轉頁面 重定向

            //將使用者資訊存入session 內建物件之一  伺服器可以為每個使用者建立一個會話物件(session)
            //1、session是內建物件不需要你建立  2、由伺服器建立
            //獲取session
            HttpSession session = request.getSession();
            // 獲取session ID
            String sessionId = session.getId();
            //設定session 生命週期 單位秒
            session.setMaxInactiveInterval(30 * 60);
            //將學生物件儲存在session中
            session.setAttribute("USER", student);
            //學生登入成功就去查詢此學生並將展示結果到index.jsp頁面上,跳轉到此頁面
            queryAllStudent(request,response);


        } else {
            //登陸失敗
            //重定向到登陸頁面
            request.setAttribute("mag", "登入失敗,賬號或者密碼錯誤");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }

    }

    private void queryAllStudent(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException {
        //獲取使用者在頁面輸入的內容
        String stuNo = request.getAttribute("StuNo").toString();
        String stuName = request.getAttribute("StuName").toString();
        String gradeId = request.getAttribute("gradeId").toString();
        List<Student> students = studentService.queryStudent(stuNo, stuName, gradeId);
        request.setAttribute("Students",students);
        request.getRequestDispatcher("index.jsp").forward(request,response);

    }

  • index.jsp頁面:使用form表單提交使用者輸入的資訊到userservlet中對應index=3所要執行的查詢學生資訊方法,迴圈遍歷查詢的學生結果並展示
  • 會遇到的問題:使用者在查詢學生完成之後,使用者在網頁的查詢資訊不能滯留,因此需要在UserServlet中將獲取到的使用者查詢時輸入的資訊再次儲存到request中並請求轉發展示到頁面上。
    使用者請求servlet,sertlet請求資料問業務層要,業務層問資料訪問層要
<%@ page import="entity.Student" %>
<%@ page import="java.util.List" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.text.ParseException" %><%--
  Created by IntelliJ IDEA.
  User: scq
  Date: 2020/11/19
  Time: 22:17
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <%
      //使用者登入:獲取servlet傳遞過來的值
      Student student = (Student) session.getAttribute("USER");
      if (null==student){response.sendRedirect("login.jsp");}//如果學生物件為空就跳轉到登入頁面
      else ;
  %>
<%--  通過表示式顯示內容,UserServlet?index=2表示向UserServlet傳入引數index為2,UserServlet通過request獲取頁面傳來的引數index--%>
  <%= student.getStudentName()%> <a href="UserServlet?index=2" >登出</a>
  </body>
<form method="post" action="UserServlet?index=3">
    <tr>
        <td>
            <%--  此處name屬性為值為request的鍵,request通過此鍵就可以獲取使用者輸入的內容--%>
<%-- 將使用者輸入的資料再通過servlet儲存之後帶回到頁面上 --%>
            <input type="text" name="StuNo" value="<%= request.getAttribute("StuNo")!=null?request.getAttribute("StuNo"):""%>">
            <input type="text" name="StuName" value="<%= request.getAttribute("StuName")!=null?request.getAttribute("StuName"):""%>">
            <select name="gradeId">
<%--                先做非空處理,否則顯示null --%>
                <%if (null!=request.getAttribute("gradeId")&& request.getAttribute("gradeId").toString().equals("1")){%>
                <option selected value="1">一年級</option>
                <%} else {%>
                <option value="1">1年級</option>
                <%}%>

                <% if (null != request.getAttribute("gradeId") && request.getAttribute("gradeId").toString().equals("2")) { %>
                <option selected value="2">二年級</option>
                <% } else {%>
                <option value="2">二年級</option>
                <%} %>

                <%if (null != request.getAttribute("gradeId") && request.getAttribute("gradeId").toString().equals("3")){%>
                <option selected value="3">3年級</option>
                <%}else{%>
                <option value="3">3年級</option>
                <%}%>
            </select>
            <input type="submit">
        </td>
    </tr>



</form>
  <tr>
      <th>學生編號</th>
      <th>學生姓名</th>
      <th>學生性別</th>
      <th>年級名稱</th>
      <th>學生生日</th>
      <th>學生電話</th>
      <th>學生地址</th>
      <th>學生郵箱</th>
      <th>學生身份證號碼</th>
  </tr>
<%
    //獲取查詢到的學生集合
    if (null!= request.getAttribute("Students")){
   List<Student> students = (List<Student>) request.getAttribute("Students");
    for (Student stu:students){
%>
  <tr>
      <td><%=stu.getStudentNo() %>
      </td>
      <td><%=stu.getStudentName() %>
      </td>
      <td><%=stu.getSex() %>
      </td>
      <td><%=stu.getGradeName() %>
      </td>
      <td><%= dateFormat(stu.getBornDate()) %>
      </td>
      <td><%=stu.getPhone() %>
      </td>
      <td><%=stu.getAddress() %>
      </td>
      <td><%=stu.getEmail() == null ? "暫無資料" : stu.getEmail() %>
      </td>
      <td><%=stu.getIdentityCard() == null ? "暫無資料" : stu.getIdentityCard() %>
      </td>
  </tr>

  <%
    }
    }

%>
<%--宣告使用: 用於宣告變數或者定義方法:此處定義一個轉化時間格式的方法--%>
<%!
  String dateFormat(String date) throws ParseException {
      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
      return simpleDateFormat.format(simpleDateFormat.parse(date));//此處把字串date轉換為時間型別再轉換為自定義時間格式
  }
%>
</html>

同一個servlet下增加分頁的功能

第一次進入login.jsp頁面,登陸成功後跳轉index.jsp頁面,此時當前頁pageNum為null,因此設定為第一頁,然後servlet查詢學生資訊,返回給前臺,前臺展示;當點選上一頁或者下一頁時,同時訪問servlet,並傳入當前頁引數與使用者查詢條件,servlet返回查詢結果給前臺,前臺展示。

  • 寫一個分頁工具類
package utils;

import java.util.List;

public class PageUtil<T> {
    private int pageNum; //當前頁
    private  int pageSize;//每頁顯示條數
    private  int pageCount;//總頁數
    private int  sumCount;//總條數
    private List<T> list; //每頁顯示的資料

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getPageCount() {
        return pageCount;
    }

    public void setPageCount(int pageCount) {
        this.pageCount = pageCount;
    }

    public int getSumCount() {
        return sumCount;
    }

    public void setSumCount(int sumCount) {
        this.sumCount = sumCount;
        if (sumCount%pageSize==0){
            pageCount=sumCount/pageSize; //總頁數=總條數/每頁顯示條數
        }else{pageCount=sumCount/pageSize+1; }
    }

    public List<T> getList() {
        return list;
    }

    public void setList(List<T> list) {
        this.list = list;
    }
}

  • Dao層介面建立計算總條數的方法,實現類實現該方法,此處需要注意的是分頁查詢需要攜帶條件,因此sql語句的編寫需要加入條件
  • Dao層查詢學生資訊的方法中需要加入當前頁數pageNum,和每頁顯示條數PageSize這兩個引數用於分頁,因此介面加入2個引數,實現類需要修改sql語句顯示分頁。

介面:

/**
     * 查詢學生資訊
     * @param stuNo 學生編號
     * @param stuName 學生姓名
     * @param gradeId 年級
     * @return 學生集合
     */
    List<Student> queryAllStudent(String stuNo,String stuName,String gradeId,int pageNum,int pageSize);

//計算總條數
    int queryCountStudent(String stuNo,String stuName,String gradeId);

實現類:

    public List<Student> queryAll(String stuNo, String stuName, String gradeId,int pageNum,int pageSize) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("SELECT * ");
        stringBuffer.append("        FROM student ");
        stringBuffer.append("JOIN grade ");
        stringBuffer.append("on grade.gradeID= student.gradeId ");
        stringBuffer.append("WHERE 1=1 ");
        if (stuNo!=null&&!stuNo.equals(""))
        stringBuffer.append("AND studentNo= "+stuNo );
        if (stuName!=null&&!stuName.equals(""))
        stringBuffer.append("AND studentName like  '%"+stuName+"%'  " );
        if (gradeId!=null&&!gradeId.equals(""))
        stringBuffer.append("        AND grade.gradeID= "+gradeId );


        // 計算分頁引數 (當前頁-1)*每頁顯示條數就是每一頁第一個資料下標
        stringBuffer.append(" LIMIT "+(pageNum-1)*pageSize+", "+pageSize+"");
        Connection connection = getConnection();

        ResultSet rs = executeQuery(connection, stringBuffer.toString());
        ArrayList<Student> studentList = new ArrayList<>();
        try {
            while (rs.next()){
                studentList.add( new Student(
 //學生物件屬性根據資料庫中student表中的設計表屬性順序新增,因此通過資料庫獲取的學生資訊也跟Student類的屬性順序一致,
                                rs.getInt("studentNo"),
                                rs.getString("loginPwd"),
                                rs.getString("studentName"),
                                rs.getString("sex"),
                                rs.getInt("gradeId"),
                                rs.getString("phone"),
                                rs.getString("address"),
                                rs.getString("bornDate"),
                                rs.getString("email"),
                                rs.getString("identityCard"),
                                rs.getString("gradeName")
                        )

                );
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            closeAll(connection,null,rs);
        }


        return studentList;
    }

    public int queryCountStudent(String stuNo, S
    tring stuName, String gradeId) {
        //分頁也需要攜帶條件
        StringBuffer sql = new StringBuffer();
        sql.append("select count(1) from student ");
        sql.append("where 1=1 ");
        if(stuNo!=null && !stuNo.equals("")){
            sql.append("AND  studentNo = "+ stuNo );
        }
        if(stuName!=null && !stuName.equals("")){
            sql.append("AND studentName like '%"+stuName+"%' ");
        }
        if(gradeId!=null && !gradeId.equals("")){
            sql.append("AND grade.gradeID=  "+gradeId);
        }
        Connection connection = getConnection();
        ResultSet rs = executeQuery(connection, sql.toString());

        int count = 0;
        try {
            while (rs.next()) {
                count = rs.getInt(1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            closeAll(connection,null,rs);
        }
        return count;
        
        
    }

  • 業務層,介面定義配合查詢顯示分頁獲取符合條件的總學生人數的方法,實現類實現具體方法
  • 由於Dao層加入分頁功能,因此業務層介面需要查詢學生資訊的方法需要加入當前頁和每頁顯示條數,實現類去呼叫Dao層的方法即可。

介面:


 /**
     * 通過 學生編號  姓名  年級  多條件查詢學生資訊
     * @param stuNo
     * @param stuName
     * @param gradeId
     * @return 學生集合
     */
    List<Student> queryStudent(String stuNo, String stuName, String gradeId,int pageNum,int pageSize);

 /**
  
     * 用於配合查詢方法 進行分頁處理  獲取符合條件的學生總數
     * @param stuNo
     * @param stuName
     * @param gradeId
     * @return
     */
    int queryCountStudent(String stuNo,String stuName,String gradeId);

實現類:

 public List<Student> queryStudent(String stuNo, String stuName, String gradeId,int pageNum,int pageSize) {
        List<Student> students = sDao.queryAll(stuNo,stuName,gradeId,pageNum,pageSize);
        return students;
    }
    
public int queryCountStudent(String stuNo, String stuName, String gradeId) {
        int count = sDao.queryCountStudent(stuNo, stuName, gradeId);
        return count;
    }
  • servlet:
    整體邏輯如下: 獲取使用者在頁面輸入的內容,將獲取到的使用者查詢時輸入的資訊再次儲存到request中;獲取分頁工具類,通過工具類的各種頁面引數查詢學生,將查詢結果放入分頁工具類的List中,一次性傳入頁面。之後頁面去遍歷工具類來分頁展示結果。
    要注意的問題如下: 從頁面獲取當前頁,如果第一次進入login.jsp,執行此函式要獲取PageNum是沒有的,因此需要判斷,如果為null就賦值為1,表示進入首頁;
    當總頁數計算出後才能判斷從前臺傳來的當前頁的頁數範圍是否在1–總頁數之間;
package servlet;

import entity.Student;
import org.apache.catalina.Session;
import service.StudentService;
import service.impl.StudentServiceImpl;
import utils.PageUtil;

import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;

public class UserServlet extends javax.servlet.http.HttpServlet {
    @Override
    public void init() throws ServletException {
        //初始化會先執行,再執行doPost或者Doget方法
        super.init();
    }

    private StudentService studentService = new StudentServiceImpl();

    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        System.out.println("這是DoPost方法");//後臺輸出
        /*1、在http協議傳輸中只能用ISO-8859-1  會產生中文亂碼*/
        /*設定request字元編碼集合*/
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        //設定傳過來的內容可能是文字text或者html
        response.setContentType("text/html;charset=utf-8");

        switch (request.getParameter("index")) {
            case "1":
                login(request, response);
                break;
            case "2":
                logout(request, response);
                break;
            case "3":
                queryAllStudent(request, response);
                break;

            default:
                break;
        }


    }



    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        doPost(request, response);
    }

    @Override
    public void destroy() {
        //銷燬方法必須手動呼叫,用於資源釋放,例如資料庫的關閉
        super.destroy();
    }

    /**
     * 為了讓servlet處理多個請求 所以建立不同的方法
     *
     * @param request
     * @param response
     */

    private void login(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException {
        //獲取使用者輸入的內容
        String userCode = request.getParameter("userCode");
        String userPwd = request.getParameter("userPwd");

//通過業務層呼叫方法 查詢學生(賬號 密碼)資訊 返回一個學生物件
        Student student = studentService.studentLogin(userCode, userPwd);
        if (null != student) {
            // 登陸成功
            //跳轉到index.jsp中
            //兩種方式 跳轉頁面 1、請求轉發  2、重定向
            //如果需要帶有值 跳轉到頁面 請求轉發
            //如果不需要帶有值 跳轉頁面 重定向

            //將使用者資訊存入session 內建物件之一  伺服器可以為每個使用者建立一個會話物件(session)
            //1、session是內建物件不需要你建立  2、由伺服器建立
            //獲取session
            HttpSession session = request.getSession();
            // 獲取session ID
            String sessionId = session.getId();
            //設定session 生命週期 單位秒
            session.setMaxInactiveInterval(30 * 60);
            //將學生物件儲存在session中
            session.setAttribute("USER", student);
            //學生登入成功就去查詢此學生並將展示結果到index.jsp頁面上,跳轉到此頁面
            queryAllStudent(request,response);


        } else {
            //登陸失敗
            //重定向到登陸頁面
            request.setAttribute("mag", "登入失敗,賬號或者密碼錯誤");
            request.getRequestDispatcher("login.jsp").forward(request, response);
        }

    }

    /**
     * 用於登出  讓session失效
     *
     * @param request
     * @param response
     * @throws IOException
     * @throws ServletException
     */
    private void logout(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws IOException {
        HttpSession session = request.getSession();
        Student student = (Student) session.getAttribute("USER");
        session.invalidate();//設定session失效
        response.sendRedirect("login.jsp");

    }

    private void queryAllStudent(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws ServletException, IOException {
        //獲取使用者在頁面輸入的內容 後臺獲取前臺的引數通過getParameter與前臺設定的引數名來獲取引數值,前臺獲取後臺的引數通過request.getAttribute來獲取
        String stuNo = request.getParameter("StuNo").toString();
        String stuName = request.getParameter("StuName").toString();
        String gradeId = request.getParameter("gradeId").toString();

        //將獲取到的使用者查詢時輸入的資訊再次儲存到request中並請求轉發展示到頁面上。
        request.setAttribute("StuNo",stuNo);
        request.setAttribute("StuName",stuName);
        request.setAttribute("gradeId",gradeId);

        //獲取分頁工具類,指定泛型為Student型別
        PageUtil<Student> pageUtil = new PageUtil();

        Integer pageNum=null;
        // 從頁面獲取當前頁,如果第一次進入login.jsp,執行此函式要獲取PageNum是沒有的,因此需要判斷,如果為null就賦值為1,表示進入首頁
        if (null==request.getParameter("PageNum")){pageNum=1;}else
        { pageNum = Integer.parseInt(request.getParameter("PageNum").toString());}

        pageUtil.setPageSize(5);//設定每頁顯示條數為5條
        //通過資料庫查詢獲取總條數,同時也計算出了總頁數pageUtil.getPageCount();
        pageUtil.setSumCount(studentService.queryCountStudent(stuNo,stuName,gradeId));

        // 當總頁數計算出後才能判斷從前臺傳來的當前頁的頁數範圍是否在1--總頁數之間
        if (pageNum<1)pageNum=1;
        else if (pageNum>pageUtil.getPageCount())pageNum=pageUtil.getPageCount();

        //設定當前頁
         pageUtil.setPageNum(pageNum);

        //通過工具類分頁查詢學生
        List<Student> students = studentService.queryStudent(stuNo, stuName, gradeId,pageUtil.getPageNum(),pageUtil.getPageSize());
        //將查詢結果放入分頁工具類的List中,一次性傳入頁面,因為頁面需要知道當前頁數總頁數
        pageUtil.setList(students);
        //由於
        request.setAttribute("pageUtil",pageUtil);
        request.getRequestDispatcher("index.jsp").forward(request,response);

    }

}

  • jsp:
    上一頁下一頁進行檢視資訊時也在訪問servlet,但是沒有加入查詢條件的話,不斷點選上一頁或者下一頁就會出現所有學生的資訊,這是因為前臺沒有將查詢條件在點選上一頁或者下一頁傳入後臺(request儲存的值指在一次請求轉發中有效,第二次請求轉發儲存的值為空),後臺獲取的引數為空,就會顯示所有的資訊。
    使用者在前臺輸入查詢學生資訊,後臺獲取使用者輸入的內容,並儲存下來,前臺再次獲取使用者輸入的內容並在點選上一頁或者下一頁的時候傳入使用者查詢的資訊(再次通過request傳值),servlet接收到使用者查詢的資訊就會顯示相應的內容,而不是因為null來展示查詢所有的內容。
<%@ page import="entity.Student" %>
<%@ page import="java.util.List" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.text.ParseException" %>
<%@ page import="utils.PageUtil" %><%--
  Created by IntelliJ IDEA.
  User: scq
  Date: 2020/11/19
  Time: 22:17
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <%
      //使用者登入:獲取servlet傳遞過來的值
      Student student = (Student) session.getAttribute("USER");
      if (null==student){response.sendRedirect("login.jsp");}//如果學生物件為空就跳轉到登入頁面
      else ;
  %>
<%--  通過表示式顯示內容,UserServlet?index=2表示向UserServlet傳入引數index為2,UserServlet通過request獲取頁面傳來的引數index--%>
  <%= student.getStudentName()%> <a href="UserServlet?index=2" >登出</a>

<form method="post" action="UserServlet?index=3">
    <tr>
        <td>
            <%--  此處name屬性為值為request的鍵,request通過此鍵就可以獲取使用者輸入的內容--%>
<%-- 將使用者輸入的資料再通過servlet儲存之後帶回到頁面上 --%>
            <input type="text" name="StuNo" value="<%= request.getAttribute("StuNo")!=null?request.getAttribute("StuNo"):""%>">
            <input type="text" name="StuName" value="<%= request.getAttribute("StuName")!=null?request.getAttribute("StuName"):""%>">
            <select name="gradeId">
<%--                先做非空處理,否則顯示null --%>
                <%if (null!=request.getAttribute("gradeId")&& request.getAttribute("gradeId").toString().equals("1")){%>
                <option selected value="1">一年級</option>
                <%} else {%>
                <option value="1">1年級</option>
                <%}%>

                <% if (null != request.getAttribute("gradeId") && request.getAttribute("gradeId").toString().equals("2")) { %>
                <option selected value="2">二年級</option>
                <% } else {%>
                <option value="2">二年級</option>
                <%} %>

                <%if (null != request.getAttribute("gradeId") && request.getAttribute("gradeId").toString().equals("3")){%>
                <option selected value="3">3年級</option>
                <%}else{%>
                <option value="3">3年級</option>
                <%}%>
            </select>
            <input type="submit">
        </td>
    </tr>



</form>
  <tr>
      <th>學生編號</th>
      <th>學生姓名</th>
      <th>學生性別</th>
      <th>年級名稱</th>
      <th>學生生日</th>
      <th>學生電話</th>
      <th>學生地址</th>
      <th>學生郵箱</th>
      <th>學生身份證號碼</th>
  </tr>
<%
    //宣告分頁工具類物件
    PageUtil<Student> pageUtil=null;
    //通過分頁工具類獲取查詢到的學生集合
    if (null!= request.getAttribute("pageUtil")){
   pageUtil =((PageUtil) request.getAttribute("pageUtil"));
        List<Student> studentList = pageUtil.getList();
        for (Student stu:studentList){
%>
  <tr>
      <td><%=stu.getStudentNo() %>
      </td>
      <td><%=stu.getStudentName() %>
      </td>
      <td><%=stu.getSex() %>
      </td>
      <td><%=stu.getGradeName() %>
      </td>
      <td><%= dateFormat(stu.getBornDate()) %>
      </td>
      <td><%=stu.getPhone() %>
      </td>
      <td><%=stu.getAddress() %>
      </td>
      <td><%=stu.getEmail() == null ? "暫無資料" : stu.getEmail() %>
      </td>
      <td><%=stu.getIdentityCard() == null ? "暫無資料" : stu.getIdentityCard() %>
      </td>
  </tr>

  <%
    }
    }

%>
<%--宣告使用: 用於宣告變數或者定義方法:此處定義一個轉化時間格式的方法--%>
<%!
  String dateFormat(String date) throws ParseException {
      SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
      return simpleDateFormat.format(simpleDateFormat.parse(date));//此處把字串date轉換為時間型別再轉換為自定義時間格式
  }
%>

<%--  如果查詢結果只有一頁就不需要顯示上一頁下一頁,只顯示內容即可--%>
<%if (pageUtil.getPageCount()>1){%>

     <tr>
    <td colspan="9">
  <%-- 當前頁/總頁數--%>
  <%=pageUtil.getPageNum()%>  / <%=pageUtil.getPageCount()%>
  <%-- index=3&PageNum=1 &表示拼接,向後臺傳多個引數時使用,將當前頁等於1傳給後臺 後臺獲取引數查詢內容再返回給前臺--%>
  <a href="UserServlet?index=3&PageNum=1">首頁</a>
  <a href="UserServlet?index=3&PageNum=<%=pageUtil.getPageNum()-1%>&StuNo=<%=request.getAttribute("StuNo")%>&StuName=<%=request.getAttribute("StuName")%>&gradeId=<%=request.getAttribute("gradeId")%>">上一頁</a>
  <a href="UserServlet?index=3&PageNum=<%=pageUtil.getPageNum()+1%>&StuNo=<%=request.getAttribute("StuNo")%>&StuName=<%=request.getAttribute("StuName")%>&gradeId=<%=request.getAttribute("gradeId")%>">下一頁</a>
  <a href="UserServlet?index=3&PageNum=<%=pageUtil.getPageCount()%>&StuNo=<%=request.getAttribute("StuNo")%>&StuName=<%=request.getAttribute("StuName")%>&gradeId=<%=request.getAttribute("gradeId")%>">尾頁</a>
  </td>

  </tr>

  <%} %>


</body>
</html>

通過計算訪問頁面次數對伺服器四個內建儲存物件理解

      pageContext.setAttribute(); 儲存的值 只在當前頁面有效
      request.setAttribute();     儲存的值 在請求轉發中有效 (這一次請求中有效)(注意:request會拼接)
      session.setAttribute();     會話級別,在這次會話訪問中有效
      application.setAttribute(); 整個tomcat中有效

test.jsp中:理解session儲存的值的有效性,不斷重新整理頁面count值++,頁面顯示人數增加,因為session儲存的是會話級值,在一次客戶端與服務端的互動中,此值一直存在,也就是說當使用者退出登入(session失效),值失效;如果session換為request,那麼每一次重新整理頁面,頁面顯示人數都是1,因為request指在轉發頁面中儲存值,每重新整理一次頁面都是一個新的值,因此值無法累加。

<%--
  Created by IntelliJ IDEA.
  User: scq
  Date: 2020/11/20
  Time: 8:04
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>test</title>
</head>
<body>
<%
int count=1;
if (null!=session.getAttribute("number")){
   count = Integer.parseInt(session.getAttribute("number").toString());
   count++;
   session.setAttribute("number",count);


}else{
    session.setAttribute("number",count);
}




%>
當前訪問人數:<%session.getAttribute("number")%>
</body>
</html>

tomcat與servlet的關係圖

tomcat響應給客戶端請求的過程形式
在這裡插入圖片描述

JSP執行過程(面試)

當客戶端呼叫index.jsp,應用程式伺服器根據index.jsp生成一個index.jsp.java檔案,再根據這個java檔案編譯成class檔案,將這個class檔案告訴應用程式伺服器。
在這裡插入圖片描述

在這裡插入圖片描述

相關文章