天天看點

Servlet體系結構

一. Servlet體系結構

Servlet – 接口

GenericServlet – 抽象類

HttpServlet – 抽象類

  • 由于Servlet接口中好多方法用不到, 是以GenericServlet對該接口的不常用方法做了預設空實作, 隻将service()方法作為抽象方法等子類去實作.
  • HTTPServlet : 對http協定的一種封裝, 簡化操作 — 可以定義類繼承HTTPServlet類, 覆寫doGet/doPost方法

二. Request對象

2.1 擷取請求體資料

1.擷取請求方式 : getMethod()

2.擷取虛拟目錄 : getContextPath()

3.擷取Servlet路徑 : getServletPath()

4.擷取get方式請求參數 : getQueryString()

擷取post方式請求參數 : getReader()

5.擷取請求URI : getRequestURI() / getRequestURL()

6.擷取協定及版本 : getProtocol()

7.擷取用戶端的IP位址 : getRemoteAddr()

2.2 擷取請求頭資料

1.擷取所有請求頭名稱 : getHeaderName()

2.擷取指定的請求頭資料 : getHeader(String name)

2.3 擷取請求參數通用方式

1.String getParameter(String name) : 根據參數名稱擷取參數值

2.String[] getParameterValues(String name) : 根據參數名稱擷取參數值的數組

3.Enumeration getParameterNames() : 擷取所有請求的參數名稱

4.Map<String, String[]) getParameterMap() : 擷取所有參數的map集合

@WebServlet("/requestdemo6")
public class RequestDemo6 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // post 擷取請求參數

        // 根據參數名稱擷取參數值
        String username = request.getParameter("username");
        System.out.println("post方式");
        System.out.println(username);

        // 根據參數名稱擷取參數值的數組
        String[] hobbies = request.getParameterValues("hobby");
        for (String hobby : hobbies) {
            System.out.println(hobby);
        }

        // 擷取所有請求的參數名稱
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String key = parameterNames.nextElement();
            String[] names = request.getParameterValues(key);
            System.out.println(key);

            for (String name : names) {
                System.out.println(name);
            }
            System.out.println("--------------");
        }

        // 擷取所有參數的map集合
        Map<String, String[]> parameterMap = request.getParameterMap();
        // 周遊
        Set<String> keySet = parameterMap.keySet();
        for (String key : keySet) {
            // 根據鍵, 擷取值
            String[] values = parameterMap.get(key);
            System.out.println(key);
            for (String value : values) {
                System.out.println(value);
            }
            System.out.println("--------------------");
        }

    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}      

2.4 中文亂碼問題

get方式 : tomcat8 已經将get方式亂碼問題解決了

post方式 : 會亂碼

​ 解決方法 : ​

​request.setCharacterEncoding("utf-8");​

2.5 請求轉發

一種在伺服器内部的資源跳轉方式

方法 : ​

​request.getRequestDispatcher("/Servlet路徑").forward(request, response);​

特點 :

  1. 浏覽器位址欄路徑不發生變化
  2. 隻能轉發到目前伺服器内部資源中
  3. 轉發是一次請求

2.6. 共享資料

  • 域對象 : 一個有作用範圍的對象, 可以再範圍内共享資料
  • request域 : 代表一次請求的範圍, 一般用于請求轉發的多個資源中共享資料
  • 方法 :
void setAttribute(String var1, Object var2);      
Object getAttribute(String var1);      
void removeAttribute(String var1);      
  • 擷取ServletContext
  • ServletContext getServletContext();

2.7 注意事項

  1. login.html中form表單的action路徑的寫法
  • 虛拟目錄 + Servlet的資源路徑
  1. BeanUtils工具類, 簡化資料封裝

    用于封裝JavaBean的, 導包時為 : ​

    ​import org.apache.commons.beanutils.BeanUtils;​

  1. 要求 :
  1. 類必須被public修飾
必須提供空參的構造器
  1. 成員變量必須使用private修飾
  2. 提供公共的setter和getter方法
  1. 功能 : 封裝資料
  2. 成員變量 :
  3. 方法 :
  1. ​setProperty(Object bean, String name, Object value)​

  2. ​getProperty(Object bean, String name)​

  3. populate(Object obj, Map map) : 将map集合的鍵值對資訊封裝到對應的JavaBean對象中

登入案例思路

Servlet體系結構

三. Response對象

功能 : 設定響應消息

  1. 設定響應行
  1. 格式 : HTTP/1.1 200 ok
  2. 設定狀态碼 : setStatus(int sc)
  1. 設定響應頭

    setHeader(String name, String value)

  2. 設定響應體
  • 使用步驟 :
  1. 擷取輸出流
  • 字元輸出流 : PrintWriter getWriter()
  • 位元組輸出流 : ServletOutputStream getOutputStream()
  1. 使用輸出流, 将資料輸出到用戶端浏覽器

3.1 轉發和重定向的差別

重定向特點 : redirect

  1. 位址欄發生變化
  2. 重定向可以通路其他站點(伺服器)的資源(需要寫虛拟目錄)
  3. 重定向是兩次請求. 不能使用request對象來共享資料

轉發特點 : forward

  1. 轉發位址路徑不變
  2. 轉發隻能通路目前伺服器下的資源(不用寫虛拟目錄)
  3. 轉發是一次請求, 可以使用request對象來共享資料

3.2 路徑寫法

  1. 相對路徑

    通過相對路徑不可以确定唯一資源

    如 : ./index.html

    不以 / 開頭, 以 . 開頭

    規則 : 找到目前資源和目标資源之間的相對位置關系

  • ./ : 目前目錄 (不寫代表./)
  • …/ : 後退一級目錄
  1. 絕對路徑

    通過絕對路徑可以确定唯一資源

    如 : http://localhost/test/responServlet2 -> /test/responServlet2

    以 / 開頭的路徑

    規則 : 判斷定義的路徑給誰用?

  • 給用戶端浏覽器使用 : 需要加虛拟目錄(項目的通路路徑)
  • 虛拟目錄可能會發生改變, 是以需要動态擷取
// 動态擷取虛拟目錄
String contextPath = request.getContextPath();
// 簡單重定向
response.sendRedirect(contextPath + "/responServlet2");      
Servlet體系結構

虛拟目錄為 ​

​/​

  • 給伺服器使用 : 不需要加虛拟目錄

3.3 伺服器輸出字元資料到浏覽器

步驟 :

  1. 擷取字元輸出流

    ​​

    ​PrintWriter pw = response.getWriter();​

  2. 輸出資料

    ​​

    ​pw.write("xxx");​

注意 :

  • 亂碼問題 :
  1. ​PrintWriter pw = respon.getWriter();​

    ​ 擷取的流的預設編碼是ISO-8859-1
  2. 設定該流的預設編碼
  3. 告訴浏覽器響應體使用的編碼

// 簡單形式, 設定編碼, 是在擷取流之前設定

​response.setContentType("text/html;charset=utf-8");​

3.4 伺服器輸出位元組資料到浏覽器

// 解決亂碼問題
response.setContentType("text/html;charset=utf-8");
// 擷取位元組輸出流
ServletOutputStream sos = response.getOutputStream();
// 輸出資料
sos.write("你好".getBytes("utf-8"));      

四. ServletContext對象

概念 : 代表整個web應用, 可以和程式的容器(伺服器)來通信

4.1 擷取方式

  1. 通過request對象擷取

    ​​

    ​request.getServletContext();​

  2. 通過HttpServlet擷取

    ​​

    ​this.getServletContext();​

4.2 功能

4.2.1 擷取MIME類型

  • MIME類型 : 在網際網路通信過程中定義的一種檔案資料類型
  • 格式 : 大類型/小類型 text/html image/jpeg
  • 擷取 : ​

    ​String mimeType = servletContext.getMimeType(fileName);​

4.2.2 域對象 : 共享資料

  1. ​servletContext1.setAttribute("msg", "servletContextTransmit");​

    ​ // 設定資料
  2. ​Object msg = servletContext1.getAttribute("msg");​

    ​// 擷取資料
  3. ​removeAttribute(String name)​

    ​ // 删除資料
  • ServletContext對象範圍 : 所有使用者所有請求的資料

4.2.3 擷取檔案的真實(伺服器)路徑

存放檔案的位置 :

  1. src目錄下 : ​

    ​String realPath1 = context.getRealPath("/WEB-INF/clases/a.txt");​

  2. WEB-INF下 : ​

    ​String realPath2 = context.getRealPath("/WEB-INF/c.txt");​

  3. web目錄下 : ​

    ​String realPath = context.getRealPath("b.txt");​

原理 :

Servlet體系結構

圖檔中的路徑中打開後,繼續打開 conf -> Catalina -> localhost, 裡面有ROOT.xml檔案

Servlet體系結構

該檔案實際上就是Tomcat設定的虛拟目錄的名字, 該項目設定虛拟目錄為 ​

​/​

​, 是以該檔案名稱為ROOT

打開該檔案後, 顯示如下内容

<?xml version="1.0"?>
<Context docBase="C:\Document\Daily_Java\out\artifacts\JavaWebLearn_war_exploded" path=""/>      

該路徑則為項目的部署位置

Servlet體系結構
Servlet體系結構
Servlet體系結構

這三張圖進行對比可以發現,

檔案中的檔案顯示為 -> web目錄中的檔案

項目中其他檔案夾 -> 存放在WEB-INF檔案夾内

WEB-INF檔案中的内容 -> 與其他檔案夾存放在一起, 都在WEB-INF檔案夾中

是以, 在擷取真實路徑時, 需要看檔案到底存放在什麼位置, 若是在web目錄下,則直接​

​"/xx.xx"​

​​即可, 否則, 需要添加它的額外路徑, 如 ​

​/WEB-INF/xx.xx​

​​ 或者 ​

​/WEB-INF/classes/xx.xx​

案例: 下載下傳圖檔

檔案下載下傳需求 :

  1. 頁面顯示超連結
  2. 點選超連結後彈出下載下傳提示框
  3. 完成圖檔檔案下載下傳

分析 :

  1. 超連結指向的資源如果能夠被浏覽器解析, 浏覽器中展示, 如果不能解析, 則彈出下載下傳提示框, 不滿足需求
  2. 任何資源都必須彈出下載下傳提示框
  3. 使用響應頭設定資源的打開方式 :
  • content-disposition: attachment;filename=xxx
  1. 定義頁面, 編輯超連結href屬性, 指向servlet, 傳遞資源名稱filename
  2. 定義servlet
  1. 擷取檔案名稱
  2. 使用位元組輸入流加載檔案進記憶體
  3. 指定response的響應頭 : content-disposition:attachment;filename=xxx
  4. 将資料寫出到response輸出流
  • 中文檔案問題
  • 解決 :
  1. 擷取用戶端使用的浏覽器版本資訊
  2. 根據不同的版本資訊, 設定filename的編碼方式不同
import sun.misc.BASE64Encoder;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;


public class DownLoadUtils {

    public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
        if (agent.contains("MSIE")) {
            // IE浏覽器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        } else if (agent.contains("Firefox")) {
            // 火狐浏覽器
            BASE64Encoder base64Encoder = new BASE64Encoder();
            filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
        } else {
            // 其它浏覽器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }
}      
@WebServlet("/downLoadServlet")
public class DownLoadServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 擷取請求參數, 檔案名稱
        String filename = request.getParameter("filename");
        // 2. 使用位元組輸入流加載檔案進記憶體
        // 2.1 找到檔案伺服器路徑
        ServletContext context = this.getServletContext();
        String realPath = context.getRealPath("/img/" + filename);
        // 2.2 用位元組流關聯
        FileInputStream fis = new FileInputStream(realPath);

        // 3. 設定response的響應頭
        // 3.1 設定響應頭類型 : content-type
        // 擷取檔案的mime類型
        String mimeType = context.getMimeType(filename);
        response.setHeader("content-type", mimeType);

        // 3.2 設定響應頭打開方式 : content-disposition

        // 解決中文檔案名問題
        // 1. 擷取user-agent請求頭
        String agent = request.getHeader("user-agent");
        // 2. 使用工具類方法編碼檔案名即可
        filename = DownLoadUtils.getFileName(agent, filename);

        response.setHeader("content-disposition", "attachment;filename=" + filename);

        // 4. 将輸入流的資料寫出到輸出流
        ServletOutputStream sos = response.getOutputStream();
        byte[] buff = new byte[1024 * 8];
        int len = 0;
        while ((len = fis.read(buff)) != -1) {
            sos.write(buff, 0, len);
        }
        fis.close();
    }      
<body>

<!-- href的路徑為: 虛拟路徑 + 圖檔路徑-->
<a href="/img/風景.jpg">圖檔1</a>

<hr>
<!--定義頁面, 編輯超連結href屬性, 指向servlet, 傳遞資源名稱filename-->
<a href="/downLoadServlet?filename=風景.jpg">圖檔</a>
    
</body>      

繼續閱讀