【学习笔记】Servlet

Servlet

Servlet简介

  • servlet是sun公司开发动态web的一门技术

  • sun公司在这些API中提供了一个接口叫做Sevlet

  • 如果你想开发一个Servlet程序,只需要:

    • 编写一个类,实现Servlet接口

    • 把开发好的Java类部署到web服务器

总结:把实现了Sevlet接口的java程序叫做 Servlet

Servlet有两个默认的实现类:HttpServlet、GenericServlet

第一个Servlet程序

  1. 构建一个普通的Maven项目,在这个项目工程中新建子工程,即Module

  2. Maven父子工程:

    在父工程中的pom.xml中多了

    <modules>
        <module>servlet-01</module>
    </modules>

    在子工程的pom.xml中有

    <parent>
        <artifactId>javaweb-02-servlet</artifactId>
        <groupId>com.wang</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    父项目中的jar包,子项目可以直接使用,就像是继承关系

  3. Maven环境优化

    • 修改web.xml 的版本

    • 将Maven的结构搭建完整

  4. 编写Servlet程序

    • 编写普通类

    • 继承HttpServlet

    package com.wang.servlet;
    ​
    import jakarta.servlet.ServletException;
    import jakarta.servlet.http.HttpServlet;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    ​
    public class HelloServlet extends HttpServlet {
        @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);
        }
    }

为什么重写这两个方法请看源码剖析

  1. 编写Servlet的映射

    为什么需要映射?

    我们写的是java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务器中注册我们写的Servlet,还需要给他一个浏览器能够访问的路径。

    在web.xml中注册Servlet 和 Servlet的映射

    <!--注册servlet-->
        <servlet>
            <servlet-name>hello</servlet-name>
            <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
        </servlet>
    <!--Servlet的请求路径-->
        <servlet-mapping>
            <servlet-name>hello</servlet-name>
            <url-pattern>/hello</url-pattern>
        </servlet-mapping>
    • 在servlet标签中有两个标签为:servlet-name 、servlet-class

      • servlet-name 标签中是 给这个servlet起个名字

      • servlet-class 标签是对应上面写的Servlet类,访问的是这个类的代码

    • 在servlet-mapping标签中有两个标签:servlet-name 、url-pattern

      • servlet-name就是Servlet的名字,必须和servlet标签中的servlet-name相同

      • url-pattern就是在浏览器中要访问的路径

  2. 配置Tomcat

  3. 访问

    image-20221011172714440

     

 

源码剖析

  • 在HttpServlet类中,继承了GenericServlet

    public abstract class HttpServlet extends GenericServlet implements Serializable
  • 在GenericServlet类中,实现了Servlet接口

    public abstract class GenericServlet implements Servlet, ServletConfig, Serializable

 

所以我们自己的类、HttpSevlet、GenericServlet、Servlet的关系如下图:

image-20221011164607243

所以我们自己写的类只需要继承HttpServlet 即可

 

在Serlet中有一些方法:

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;
​
    ServletConfig getServletConfig();
​
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
​
    String getServletInfo();
​
    void destroy();
}

主要的方法是 service()

在GenericServelt中,没有对service() 进行处理:

public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

 

在HttpServlet中实现了service()方法:

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            this.doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader("If-Modified-Since");
            if (ifModifiedSince < lastModified / 1000L * 1000L) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
    } else if (method.equals("POST")) {
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
        this.doTrace(req, resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }
​
}

我们发现在service() 中 是对请求方式的判断 如 get、post等

在HttpServlet中,有这些请求方式默认的方法,如 doGet()、doPost()等

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String protocol = req.getProtocol();
    String msg = lStrings.getString("http.method_get_not_supported");
    if (protocol.endsWith("1.1")) {
        resp.sendError(405, msg);
    } else {
        resp.sendError(400, msg);
    }
​
}
​
protected long getLastModified(HttpServletRequest req) {
    return -1L;
}
​
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    NoBodyResponse response = new NoBodyResponse(resp);
    this.doGet(req, response);
    response.setContentLength();
}
​
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String protocol = req.getProtocol();
    String msg = lStrings.getString("http.method_post_not_supported");
    if (protocol.endsWith("1.1")) {
        resp.sendError(405, msg);
    } else {
        resp.sendError(400, msg);
    }
​
}

所以我们要做的事情,就是重写这些方法

我们主要重写 doGet()、doPost()

它们两个只是请求实现不同的方式,可以相互调用,因为业务逻辑相同

所以我们可以在一个里面调用另一个

 

Servlet 原理

Servlet是由web服务器调用,web服务器收到请求后,要进行以下操作:

image-20221011190046402

 

Mapping问题

一个Servlet可以指定一个映射路径

<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

 

一个Servlet可以指定多个映射路径

<!--注册servlet-->
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello3</url-pattern>
</servlet-mapping>

在浏览器的url中,可以输入 /hello /hello2 /hello3 都能够进入hello这个Servlet

 

一个Servlet可以指定通用的映射路径

使用通配符 *

<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.wang.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello/*</url-pattern>
</servlet-mapping>

在浏览器的url中,可以输入/hello/* * 可以代表任何请求,都可以成功

 

默认映射路径

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

浏览器一访问就进入hello这个Servlet,是默认的,把之前的index.jsp顶替了

指定前缀或后缀的映射路径

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

可以自定义前缀或后缀,访问时只要后缀为.do就可以访问

注意:* 前面不能加映射路径

 

优先级问题

指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求

 

举例:

新创建一个Servlet 叫做ErrorServlet

package com.wang.servlet;
​
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.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("test/html");
        resp.setCharacterEncoding("utf-8");
        PrintWriter writer = resp.getWriter();
        writer.print("<h1>肆零肆</h1>");
    }
​
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

它的映射路径为

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

就是默认的映射路径

我们运行一下

项目的index.jsp被替换成了 这个Servlet

image-20221011194555727

 

如果我们访问一下/hello 呢?

image-20221011194626111

结果就是,依旧是HelloServlet 那个 Servlet

image-20221011194700749

如果任意访问,就会使用默认的/* 映射路径

 

posted @ 2022-10-11 19:48  GrowthRoad  阅读(24)  评论(0编辑  收藏  举报