1. 程式人生 > 其它 >Servlet基礎使用

Servlet基礎使用

程式設計師學習一門新的技術,永遠繞不開一個永恆的話題——Hello World!,在我們學習servlet的時候也首先來實現一個Hello World!入門程式!

Hello World!入門程式

首先編寫一個Servlet類,在其中返回hello world!

public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("utf-8");
        PrintWriter out = resp.getWriter();
        out.write("hello world!");
        out.flush();
        out.close();
    }
    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

然後需要配置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">
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    <servlet>
        <!-- servlet名字 -->
        <servlet-name>helloworldServlet</servlet-name>
        <!-- servlet的類路徑,會將這個類命名為上面配置的名字 -->
        <servlet-class>cn.fzkj.servlet.IndexServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloworldServlet</servlet-name>
        <!-- 攔截路徑,當訪問hello的時候就會交給這個servlet處理 -->
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

這樣配置了之後一個簡單的hello world入門程式就完成了。

servlet3.0版本中,將xml配置進一步簡化了,可以使用註解來完成。上面的servlet程式碼就可以修改為:

// 標註這是一個servlet,名字叫helloworldServlet,攔截的路徑是/hello,這個註解的效果和之前的xml配置一樣。
@WebServlet(name = "helloworldServlet", urlPatterns = "/hello")
public class IndexServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("utf-8");
        PrintWriter out = resp.getWriter();
        out.write("hello world!");
        out.flush();
        out.close();
    }
    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

servlet

Java Servlet 是執行在 Web 伺服器或應用伺服器上的程式,它是作為來自 Web 瀏覽器或其他 HTTP 客戶端的請求和 HTTP 伺服器上的資料庫或應用程式之間的中間層。 ——菜鳥教程

http請求通過servlet來和程式進行互動。

servlet生命週期

servlet生命週期指的是servlet從建立到銷燬的整個過程。它遵循以下的幾個過程:

  • 初始化後會呼叫init()方法
  • 呼叫service()方法來處理客戶端請求
  • 銷燬前呼叫destroy()方法
  • 最後由垃圾回收器回收
建立servlet的方式

建立servlet有三種方式,這裡介紹其中兩種比較常用的方式。

第一種:實現Servlet介面

public class ServletDemo1 implements Servlet {

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("=== 初始化執行 ===");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("=== 處理業務邏輯 ===");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("=== 銷燬執行 ===");
    }
}

*第二種:繼承HttpServlet

其實通過檢視原始碼可以發現,HttpServlet類最終也是實現了Servlet介面,只是對Servlet中一些方法做了實現。

public abstract class HttpServlet extends GenericServlet {}
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {}

使用HttpServlet類建立

@WebServlet(name = "httpServletDemo", urlPatterns = "/hello")
public class ServletHttpDemo extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("hello world");
    }

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

HttpServletServlet介面做了實現,同時提供了一些用於處理http請求的方法,在繼承了HttpServlet類之後就可以非常方便的處理使用者請求,而不用在去關心其他。

手寫一個HttpServlet

根據上面的發現,我們自己也可以來對Servlet介面進行一個實現,來達到和HttpServlet類相似的功能。說幹就幹。

由於Servlet中實際會呼叫Service方法進行業務處理,那就需要將該方法進行重寫,讓它支援http請求。

/**
 * @DESCRIPTION: 繼承了servlet介面,實現類似於HttpServlet相似的功能
 * @AUTHOR: AnotherOne
 * @DATE: 2021/7/14 16:23
 */
public class BaseServlet implements Servlet {

    public BaseServlet() {
        // do nothing
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("=== BaseServlet ===");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    // 重寫該方法
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest) servletRequest;
            response = (HttpServletResponse) servletResponse;
            this.service(request, response);
        }catch (Exception e){
            System.out.println("無效的請求");
        }
    }

    // 實現不同請求方式的判斷與業務的轉發
    protected void service(HttpServletRequest res, HttpServletResponse rep) throws ServletException, IOException {
        String method = res.getMethod();
        if (HttpType.GET.name().equalsIgnoreCase(method)){
            this.doGet(res, rep);
        } else if (HttpType.POST.name().equalsIgnoreCase(method)){
            this.doPost(res, rep);
        } else {
            System.out.println("暫不支援的請求型別");
        }
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("=== BaseServlet ===");
    }

    protected void doGet(ServletRequest req, ServletResponse res){
        System.out.println("BaseServlet get 預設實現");
    }

    protected void doPost(ServletRequest req, ServletResponse res){
        System.out.println("BaseServlet post 預設實現");
    }

    /**
     * 定義http請求列舉類
     */
     enum HttpType {
        GET,
        POST,
        PUT,
        DELETE
    }
}

接下來來寫個業務類驗證一下。

@WebServlet(name = "servletDemo2", urlPatterns = "/demo2")
public class ServletHttpDemo2 extends BaseServlet {

    @Override
    protected void doGet(ServletRequest req, ServletResponse res) {
//        super.doGet(req, res);
        System.out.println("子類業務處理");
    }
}

當訪問/demo2時控制檯可以打印出子類業務處理,說明效果不錯。

ServletConfig物件

在容器建立Servlet例項物件的時候,會將一些初始化引數封裝到ServletConfig物件中,並且在呼叫物件的init(ServletConfig config)方法時傳遞給該例項。所以可以通過該物件得到一些初始化引數資訊。

@WebServlet(name = "httpServletDemo", urlPatterns = "/demo", initParams = {
        @WebInitParam(name = "name", value = "fzkj"),
        @WebInitParam(name = "age", value = "23")
})
public class ServletHttpDemo extends HttpServlet {

    private ServletConfig config;

    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 獲取配置的name和age屬性值
        System.out.println(config.getInitParameter("name"));
        System.out.println(config.getInitParameter("age"));
        // 獲取當前servlet名稱
        System.out.println(config.getServletName());
        // 獲取當前引數屬性名稱的列舉
        Enumeration<String> names = config.getInitParameterNames();
        while(names.hasMoreElements()){
            System.out.println(names.nextElement());
        }
        // 獲取servletcontext物件
        ServletContext servletContext = config.getServletContext();
    }

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

上面程式碼是使用註解的方式定義初始化引數。也可以使用xml配置的方式進行。

<servlet>
    <servlet-name>indeServlet</servlet-name>
    <servlet-class>cn.fzkj.servlet.IndexServlet</servlet-class>
    <init-param>
        <param-name>namespace</param-name>
        <param-value>fzkj</param-value>
    </init-param>
    <init-param>
        <param-name>age</param-name>
        <param-value>23</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>indeServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

這兩者的效果並沒有什麼不同。

上述程式碼執行結果:

fzkj
23
httpServletDemo
name
age
ServletContext物件

web容器在啟動的時候,會為每個web應用程式建立一個ServletContext物件,它代表的就是當前web應用。

所以在同一個web應用中,所有的servlet共享同一個ServletContext物件,因此可以通過這個物件來實現不同Servlet之間的通訊。

servletContext實現多個servlet之間資料共享

/**
 * @DESCRIPTION: 多個servlet資料共享 和 demo2
 * @AUTHOR: AnotherOne
 * @DATE: 2021/7/15 11:15
 */
@WebServlet(name = "demo1", urlPatterns = "/demo1")
public class Demo1 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("utf-8");
        ServletContext context = this.getServletContext();
        context.setAttribute("name", "fzkj");
        PrintWriter out = resp.getWriter();
        out.write("資料共享成功");
        out.flush();
        out.close();
    }
}

/**
 * @DESCRIPTION: TODO 多個servlet資料共享 和 demo1
 * @AUTHOR: AnotherOne
 * @DATE: 2021/7/15 11:15
 */
@WebServlet(name = "demo2", urlPatterns = "/demo2")
public class Demo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("utf-8");
        ServletContext context = this.getServletContext();
        String name = (String)context.getAttribute("name");
        PrintWriter out = resp.getWriter();
        out.write("資料讀取成功" + name);
        out.flush();
        out.close();
    }
}

讀取web應用初始化引數

/**
 * @DESCRIPTION: TODO 讀取web應用初始化引數 -> 在xml中配置<context-param></context-param>
 * @AUTHOR: AnotherOne
 * @DATE: 2021/7/15 11:28
 */
@WebServlet(name = "demo3", urlPatterns = "/demo3")
public class Demo3 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        System.out.println(context.getInitParameter("names"));
    }
}
    <context-param>
        <param-name>names</param-name>
        <param-value>fzkj123</param-value>
    </context-param>

是不是有一種可以通過註解的方式建立web初始化引數?後面再來研究

ServletContext物件實現請求轉發

/**
 * @DESCRIPTION: TODO 實現請求轉發,轉發給Demo5
 * @AUTHOR: AnotherOne
 * @DATE: 2021/7/15 11:39
 */
@WebServlet(name = "dmeo4", urlPatterns = "/demo4")
public class Demo4 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        resp.setCharacterEncoding("utf-8");
        resp.getOutputStream().write("這是再demo4中".getBytes());
        // 轉發給demo5
        RequestDispatcher rd = context.getRequestDispatcher("/demo5");
        rd.forward(req, resp);
    }
}

/**
 * @DESCRIPTION: TODO 接收demo4的轉發請求並處理
 * @AUTHOR: AnotherOne
 * @DATE: 2021/7/15 11:43
 */
@WebServlet(name = "dmeo5", urlPatterns = "/demo5")
public class Demo5 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setCharacterEncoding("utf-8");
        resp.getOutputStream().write("這是再demo5中".getBytes());
    }
}

由於再demo4的處理邏輯中將請求轉發給了demo5,所以實際上頁面會列印這是再demo5中

Filter

Servlet 過濾器可以動態地攔截請求和響應,以變換或使用包含在請求或響應中的資訊。

可以將一個或多個 Servlet 過濾器附加到一個 Servlet 或一組 Servlet。Servlet 過濾器也可以附加到 JavaServer Pages (JSP) 檔案和 HTML 頁面。呼叫 Servlet 前呼叫所有附加的 Servlet 過濾器。

Servlet 過濾器是可用於 Servlet 程式設計的 Java 類,可以實現以下目的:

  • 在客戶端的請求訪問後端資源之前,攔截這些請求。
  • 在伺服器的響應傳送回客戶端之前,處理這些響應。

根據規範建議的各種型別的過濾器:

  • 身份驗證過濾器(Authentication Filters)。
  • 資料壓縮過濾器(Data compression Filters)。
  • 加密過濾器(Encryption Filters)。
  • 觸發資源訪問事件過濾器。
  • 影象轉換過濾器(Image Conversion Filters)。
  • 日誌記錄和稽核過濾器(Logging and Auditing Filters)。
  • MIME-TYPE 鏈過濾器(MIME-TYPE Chain Filters)。
  • 標記化過濾器(Tokenizing Filters)。
  • XSL/T 過濾器(XSL/T Filters),轉換 XML 內容。

過濾器通過 Web 部署描述符(web.xml)中的 XML 標籤來宣告,然後對映到您的應用程式的部署描述符中的 Servlet 名稱或 URL 模式。

當 Web 容器啟動 Web 應用程式時,它會為您在部署描述符中宣告的每一個過濾器建立一個例項。

Filter的執行順序與在web.xml配置檔案中的配置順序一致,一般把Filter配置在所有的Servlet之前。

​ ——菜鳥教程

Filter也稱之為過濾器,它是Servlet技術中最實用的技術,Web開發人員通過Filter技術,對web伺服器管理的所有web資源:例如Jsp, Servlet, 靜態圖片檔案或靜態 html 檔案等進行攔截,從而實現一些特殊的功能。例如實現URL級別的許可權訪問控制、過濾敏感詞彙、壓縮響應資訊等一些高階功能。

它主要用於對使用者請求進行預處理,也可以對HttpServletResponse進行後處理。使用Filter的完整流程:Filter對使用者請求進行預處理,接著將請求交給Servlet進行處理並生成響應,最後Filter再對伺服器響應進行後處理。

FilterChain:在一個web應用中,可以開發編寫多個Filter,這些Filter組合起來稱之為一個Filter鏈。

web伺服器根據Filter在web.xml檔案中的註冊順序,決定先呼叫哪個Filter,當第一個Filter的doFilter方法被呼叫時,web伺服器會建立一個代表Filter鏈的FilterChain物件傳遞給該方法。在doFilter方法中,開發人員如果呼叫了FilterChain物件的doFilter方法,則web伺服器會檢查FilterChain物件中是否還有filter,如果有,則呼叫第2個filter,如果沒有,則呼叫目標資源。

建立一個簡單的過濾器

/**
 * @DESCRIPTION: TODO
 * @AUTHOR: AnotherOne
 * @DATE: 2021/7/15 12:47
 */
// 標註這是一個過濾器,urlPatterns用於配置攔截的url規則,符合規則的url才會被攔截
@WebFilter(filterName = "fzkj_filter_demo1", urlPatterns = "/*", initParams = {
        @WebInitParam(name = "name", value = "fzkj_filter_demo1"),
        @WebInitParam(name = "age", value = "23")
})
public class Demo1 implements Filter {

    private FilterConfig config;

    @Override
    public void init(FilterConfig config) throws ServletException {
        this.config = config;
        System.out.println("=== demo1 過濾器初始化 ===");
        // 獲取當前過濾器名稱
        System.out.println(config.getFilterName());
        System.out.println("=========================");
        // 獲取初始化引數‘name’的值
        System.out.println(config.getInitParameter("name"));
        System.out.println("=========================");
        // 獲取所有初始化引數的名稱
        Enumeration<String> names = config.getInitParameterNames();
        while(names.hasMoreElements()){
            System.out.println(names.nextElement());
        }
        System.out.println("=========================");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        System.out.println("經過 " + this.config.getFilterName() + " filter");
        // 交給下一過濾器
        chain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("=== demo1 過濾器銷燬 ===");
    }
}

Filter的中的方法與用法和Servlet非常相似,不在過多贅述


持續更新~~