Java之Servlet

Zh1z3ven發表於2021-07-26

Servlet

sun公司基於java開發動態web的一門技術

其中在這些API中提供了一個介面就是Servlet

servlet有兩個實現類,HttpServlet、GenericServlet,其中GenericServlet是HttpServlet的父類

在servlet3.0版本後可以不用寫web.xml檔案,可以直接使用註解定義載入。
我們直接在類上面加入註解

@WebServlet(urlPatterns = "/demo")

HelloServlet

  1. 新建一個普通專案,不要勾選maven中webapp模版,並刪掉專案中的src目錄,這個空工程就是Maven父工程

  2. 在pom.xml中匯入依賴

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>4.0.1</version>
            </dependency>
    <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version>
    </dependency>
    
    

    如果匯入失敗,手動下載jar包放在本地倉庫中

  3. 建立子modul,勾選webapp模版

  4. 子專案中pom.xml新增<parent>標籤

    關於Maven父子工程:

    在父工程的pom.xml中會多出

    <modules>
            <module>servlet01</module>
    </modules>
    

    在子工程的pom.xml中會多出(Maven3.6.0之後一開始會有,專案載入完後會被自動清除掉,自己手動新增即可)

    <parent>
        <artifactId>javaweb01</artifactId>
        <groupId>com.zh1z3ven</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    

    父專案中的jar包,子專案可以直接使用

  5. 修改子專案中的web.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                          http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0"
             metadata-complete="true">
    
    </web-app>
    
  6. 子專案中新建javaresources目錄,並修改對應目錄屬性

  7. 新建com.zh1z3ven.servlet包,編寫servlet程式

    寫一個普通類,實現servlet介面

    這裡需要注意父專案的pom.xml檔案中的<scope>標籤,注意如果父專案存在<scope>標籤且值為provided時,子modul無法成功引用父類jar包

    在Maven中依賴的域有:compile、provided、runtime、system、test、import

    一、compile(預設)

    當依賴的scope為compile的時候,那麼當前這個依賴的包,會在編譯的時候被加入進來,並且在打包(mvn package)的時候也會被加入進來。
    編譯範圍有效,在編譯與打包時都會加入進去。

    二、provided

    當依賴的scope為provided的時候,在編譯和測試的時候有效,在執行(mvn package)進行打包時不會加入。比如, 我們開發一個web應用,在編譯時我們需要依賴servlet-api.jar,但是在執行時我們不需要該 jar包,因為這個jar 包已由web伺服器提供,如果在打包時又被加入進去,那麼就可能產生衝突。此時我們就可以使用 provided 進行範圍修飾。

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    public class HelloServlet extends HttpServlet {
    
        //由於get和post只是請求相互實現的方式,可以互相呼叫,業務邏輯都一樣
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            PrintWriter writer = resp.getWriter();  //響應流
            writer.print("Hello Servlet");
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    				doGet(req,resp);
        }
    }
    

  8. 修改web.xml註冊servlet,編寫servlet對映

    <!--    註冊servlet-->
        <servlet>
            <servlet-name>hello</servlet-name>
    <!--        繫結servlet實現類-->
            <servlet-class>com.zh1z3ven.servlet.HelloServlet</servlet-class>
        </servlet>
    <!--    註冊servlet路由-->
        <servlet-mapping>
            <servlet-name>hello</servlet-name>
    <!--        設定訪問路由-->
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    
  9. 配置Tomcat,配置完後先點選apply再點選ok

  10. 啟動測試

  11. 關於target目錄

Servlet原理

客戶端請求通過瀏覽器傳送到web伺服器,請求包含請求頭請求體,之後變成request物件傳給servlet,servlet呼叫service方法處理請求並返回response。而對於Servlet來說,當伺服器處理請求時,先根據URL中的路徑查詢是否在web.xml的<servlet-mapping> (servlet對映)<url-pattern>標籤有對應的值,有的話根據此Servlet對應的類名,通過該類的例項化物件呼叫service方法處理請求並返回響應。

Servlet-Mapping

  1. 一個servlet可以指定一個對映路徑

    <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
  2. 一個servlet可以指定多個對映路徑

    <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/hello1</url-pattern>
    </servlet-mapping>
    
  3. 一個servlet可以指定通用對映路徑

    <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
    
  4. 指定一些字尾或字首等...,例如.do

    以這個字尾結尾,不管前面uri加了多少層目錄都可以訪問到

    <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    
  5. 預設請求路徑

    <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/*</url-pattern>
    </servlet-mapping>
    

處理簡單報錯Servlet

ErrorServelt

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class ErrorServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");

        PrintWriter writer = resp.getWriter();
        writer.print("<h1>404</h1>");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

Web.xml中新增,這裡<url-pattern>/*</url-pattern>雖然設定的預設萬用字元,但是url輸入其他註冊好的servlet還是會優先走對應的servlet。指定了固定路徑的對映預設優先順序最高,如果找不到才會走預設的處理請求。

<servlet>
        <servlet-name>error</servlet-name>
        <servlet-class>com.zh1z3ven.servlet.ErrorServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>error</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

ServletContext

Web容器在啟動的時候會為每個Web程式建立一個ServletContext物件,代表當前的Web應用物件。這個ServletContext可以儲存一些資料,也可以供Servlet從中讀取資料。類似於Servlet之間的中介,實現Servlet直接的資料共享。

ServletContext:Servlet上下文

可根據HttpServlet的例項化物件呼叫getServletContext獲取

ServletContext servletContext = this.getServletContext();

1、共享資料

在一個servlet中建立的資料可以在另一個servlet中拿到。ServletContext物件是可以被多個Servlet共用的

寫入資料

 servletContext.setAttribute("username",username);   //將資料寫入ServletContext中
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Hello Servlet doGet");
        //this.getInitParameter()   初始化引數
        //this.getServletConfig()   sevlet配置
        //this.getServletContext()  sevlet上下文
      	//獲得ServletContext物件
        ServletContext servletContext = this.getServletContext();   //this指代當前這個類本身

        String username = "zh1z3ven";
        servletContext.setAttribute("username",username);   //將資料寫入ServletContext中
    }
}

讀取資料

public class GetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Hello GetServlet ");
        ServletContext servletContext = this.getServletContext();
        String username = (String) servletContext.getAttribute("username"); //強制型別轉換

        PrintWriter writer = resp.getWriter();
        writer.print("<h1>username: </h1>" + username);
    }
}

Web.xml

<servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.zh1z3ven.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>getname</servlet-name>
        <servlet-class>com.zh1z3ven.servlet.GetServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>getname</servlet-name>
        <url-pattern>/getname</url-pattern>
    </servlet-mapping>

測試結果

需要先訪問/s2/hello,才會觸發HelloServlet類中的doGet方法將name寫入ServletContext中;再訪問/s2/getname時觸發GetServlet中的doGet方法去讀取ServletContext中的name並根據程式碼回顯到頁面上。

2、獲取初始化引數getInitParameter

引數為web.xml中的<context-param>下的引數

@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        String url = servletContext.getInitParameter("url");

        resp.setCharacterEncoding("utf-8");
        resp.setContentType("text/html");
        PrintWriter writer = resp.getWriter();
        writer.print(url);
    }

Web.xml

<context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
<servlet>
    <servlet-name>gp</servlet-name>
    <servlet-class>com.zh1z3ven.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>gp</servlet-name>
    <url-pattern>/gp</url-pattern>
</servlet-mapping>

3、請求轉發requestDispatcher

獲取ServletContext上下文物件,呼叫requestDispatcher的forward方法轉發請求

RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/gp");   //請求轉發目標路徑
requestDispatcher.forward(req,resp);    //呼叫forward方法轉發請求

Web.xml

<servlet>
    <servlet-name>sd4</servlet-name>
    <servlet-class>com.zh1z3ven.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>sd4</servlet-name>
    <url-pattern>/sd4</url-pattern>
</servlet-mapping>

class

public class ServletDemo04 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        //RequestDispatcher requestDispatcher = servletContext.getRequestDispatcher("/gp");   //請求轉發目標路徑
        //requestDispatcher.forward(req,resp);    //呼叫forward方法轉發請求
        servletContext.getRequestDispatcher("/gp").forward(req,resp);
    }
}

如果出現tomcat爆500的情況,可以嘗試關掉tomcat然後maven clean一下專案的target,在重啟tomcat可能就好了

4、讀取資原始檔

Properties,如果在專案的java目錄下有寫properties檔案的話預設是不會被匯出的,需要在pom.xml中多加一段build的配置,使得java目錄下的資原始檔也可以被匯出。

但是不管是resources和java目錄下的properties檔案都會被打報導WEB-INF/classes目錄下,也就是classpath路徑

思路:利用this.getServletContext().getResourceAsStream()方法返回一個流,通過Properties類的例項化物件呼叫load()方法載入這個流獲取資料。

public class PropertiesServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/zh1z3ven/servlet/test.properties");		//返回InputStream流

        Properties properties = new Properties();		//例項化Properties物件

        properties.load(is);		//loadInputStream流

        String username = properties.getProperty("username");
        String password = properties.getProperty("password");

        resp.getWriter().print(username + ":" + password);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

Web.xml

<servlet>
        <servlet-name>sd5</servlet-name>
        <servlet-class>com.zh1z3ven.servlet.PropertiesServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>sd5</servlet-name>
        <url-pattern>/sd5</url-pattern>
    </servlet-mapping>

資原始檔路徑,同理如果是在原始碼目錄下,只需要寫好相對路徑即可。

例如:此test.properties

路徑為:

/WEB-INF/classes/com/zh1z3ven/servlet/test.properties

HttpServletResponse

web伺服器會將客戶端發來的http請求進行處理,生成兩個物件HttpServletRequest和HttpServletResponse。

  • 如果要獲取客戶端發來的請求資訊:HttpServletRequest
  • 如果要處理需要響應給客戶端的資訊:HttpServletResponse

1、簡單分類

向客戶端傳送資料的方法

ServletOutputStream getOutputStream() throws IOException;		//普通輸出流

PrintWriter getWriter() throws IOException;			//處理中

向瀏覽器傳送響應頭的方法

void setCharacterEncoding(String var1);

void setContentLength(int var1);

void setContentLengthLong(long var1);

void setContentType(String var1);

void setDateHeader(String var1, long var2);

void addDateHeader(String var1, long var2);

void setHeader(String var1, String var2);

void addHeader(String var1, String var2);

void setIntHeader(String var1, int var2);

void addIntHeader(String var1, int var2);

void setStatus(int var1);		

2、常見應用

  1. 向瀏覽器輸出訊息(getWriter,getOutputStream)
  2. 下載檔案
    1. 獲取下載檔案的路徑
    2. 下載的檔名
    3. 讓瀏覽器支援我們能下載的東西
    4. 獲取下載檔案的輸入流
    5. 建立緩衝區
    6. 獲取OutputStream物件
    7. 將FileOutputStream流寫入到buffer緩衝區
    8. 使用OutputStream將buffer緩衝區的資料輸出到客戶端
public class FileServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        System.out.println("=====================123131===========================");
        //獲取下載檔案的路徑
        //String realPath = this.getServletContext().getRealPath("/1.png");//獲取絕對路徑
        String realPath = "/Users/b/Desktop/javacode/JavaWeb_Maven/javaweb-02-servlet/response/src/main/resources/1.png";
        System.out.println("下載檔案的路徑: " + realPath);

        //下載的檔名
        String fileName = realPath.substring(realPath.lastIndexOf("/") + 1);
        System.out.println(fileName);

        //讓瀏覽器支援我們能下載的東西
        resp.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        //獲取下載檔案的輸入流
        FileInputStream fis = new FileInputStream(realPath);

        //建立緩衝區
        int len = 0;
        byte[] buffer = new byte[1024];

        //獲取OutputStream物件
        ServletOutputStream servletOutputStream = resp.getOutputStream();

        //將FileOutputStream流寫入到buffer緩衝區
        while ((len = fis.read(buffer)) != -1){

            // 使用OutputStream將buffer緩衝區的資料輸出到客戶端
            servletOutputStream.write(buffer, 0, len);
        }

        //釋放資源
        servletOutputStream.close();
        fis.close();

    }

重定向

方法:

void sendRedirect(String var1) throws IOException;

Demo:

public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.sendRedirect("/download");
    }
}

重定向與轉發

重定向與轉發都會使使用者訪問其他資源,只是發生位置不同,請求轉發放生在服務端,只能訪問該專案內部資源。重定向是第一次收到使用者請求後讓客戶端重新訪問一個新的url地址,客戶一共請求了兩次。

HttpServletRequest

1、Request常用方法

String getMethod()              //獲取請求方式
String getContextPath()         //獲取虛擬路徑
String getServletPath()         //獲取Servlet路徑
String getQueryString()         //獲取get請求方式引數
String getRequestURI()          //獲取請求uri
StringBuffer getRequestURL()    //獲取URL
String getProtocol()            //獲取協議版本
String getRemoteAddr()          //獲取客戶端ip
String getHeader(String name)   //通過請求頭的名稱獲取請求頭的值
Enumeration<String> getHeaderNames() //獲取所有的請求頭名稱
  
String getParameter(String var1);		//獲取一個指定引數的值

Enumeration<String> getParameterNames();	

String[] getParameterValues(String var1);		//獲取多個引數的值,返回一個String陣列

Map<String, String[]> getParameterMap();	
public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");      //設定編碼

        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbys = req.getParameterValues("hobbys");

        System.out.println("================================");
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobbys));        //獲取string陣列
        System.out.println("================================");

        req.getRequestDispatcher("/success.jsp").forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }
}

相關文章