JavaWeb 速通Servlet(Servlet和HttpServlet)

举报
Cyan_RA9 发表于 2023/09/01 09:42:15 2023/09/01
【摘要】 JavaWeb——Servlet,HttpServlet及其底层原理 内容分享。

目录

一、Servlet快速入门

        1.为什么需要Servlet?

        2.什么是Servlet?

        3.Servlet开发说明 : 

        4.入门案例 : 

二、 Servlet生命周期

        1.浏览器请求Servlet的流程分析(重要) : 

        2.生命周期 : 

            1° 初始化阶段

            2° 处理请求阶段

            3° 终止阶段

三、Servlet基本使用

        1.Get请求和Post请求的处理 : 

        2.开发Servlet的正确姿势(HttpServlet) : 

        3.注解方式开发Servlet : 

        4.URL匹配的4种方式: 

            1° 精确匹配 

            2° 目录匹配

            3° 扩展名匹配

            4° 任意匹配

            5° Δ注意事项Δ

四、Servlet(上)总结


一、Servlet快速入门

        1.为什么需要Servlet?

        当浏览器向Web服务器请求静态web资源(HTML,CSS,JS,图片等)时,Tomcat可以对请求进行解析后,拿到资源,以响应的方式直接返回给浏览器,不与数据库发生关系。此时Tomcat只提供普通的web服务

        但是,当浏览器发出动态地与用户进行交互的请求时,比如用户留言,用户评论等直接与数据库挂钩的需求,就需要Servlet去调用其他Java程序来实现,Java程序通常会分层(Service,DAO等)。此时Tomcat作为了Servlet的容器(也称作引擎),因为Tomcat支持Servlet,能够完成对Servlet的解析

        2.什么是Servlet?

        Servlet在开发动态WEB工程中,得到广泛的应用,Servlet是SpringMVC,SpringBoot的底层基础。

        Servlet本义是java服务器小程序,特点如下——
        由服务器端调用和执行(eg : 由Tomcat解析和执行)。

        java语言编写,本质就是Java类,供其他Java程序(Servlet引擎)调用,不能独立运行。

        按照Servlet规范开发(类似于JDBC接口的规范,符合规范的Web服务器例如Tomcat, WebLogic都可以支持Servlet)。

        Servlet功能强大,可以完成几乎所有的网站功能,但对于技术栈的要求很高。

        3.Servlet开发说明 : 

        servlet3.0前使用web.xml;servlet3.0版本以后(包括3.0)支持注解(实际使用更多),同时支持web.xml配置
        PS : 原生的
Servlet在项目中使用很少。

        4.入门案例 : 

                首先,新建一个项目,引入Web Application框架支持后,在WEB-INF目录下新建lib目录。如下图所示 : 

编辑

                然后,导入Servlet所需的jar包"servlet-api.jar",在Tomcat的lib目录下,如下图所示 : 

编辑

                导入后要部署到当前项目,如下图所示 : 

编辑

                接着配置Tomcat
                然后,新建一个类,实现Servlet接口并重写Servlet接口的五个方法。
                TryServlet类代码如下 : (注意看注释

package intro;

import jakarta.servlet.*;

import java.io.IOException;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
/** 一个类只有实现了Servlet接口,才能被当作Servlet使用。 */
public class TryServlet implements Servlet {

    //创建TryServlet实例时会调用init方法,该方法只会被调用一次。
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init方法被调用");
    }

    //返回ServletConfig对象,即返回Servlet的配置信息
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /*
        (1)service方法用于处理浏览器的请求(包括get/post)
        (2)每当浏览器请求一次Servlet时,都会调用一次service方法。
        (3)当Tomcat调用该方法时,会将请求的数据封装成实现了ServletRequest接口的servletRequest对象,
            通过servletRequest对象,可以得到用户提交的数据。
        (4)servletResponse对象可以用于返回数据给Tomcat,Tomcat解析得到对应的数据后,
            会再把数据封装成HTTP响应的方式,发送给浏览器,浏览器再解析并显示。
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("This is the first Servlet try~");
    }

    //返回servlet信息,使用较少
    @Override
    public String getServletInfo() {
        return null;
    }

    //该方法是在servlet被销毁时被Tomcat调用,只调用一次
    @Override
    public void destroy() {
        System.out.println("destroy方法被调用。");
    }
}

                在web.xml中配置TryServlet:TryServlet提供对外访问地址。web.xml主要用于配置该web应用使用到的Servlet。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">
    <servlet>
        <servlet-name>TryServlet</servlet-name>
        <servlet-class>intro.TryServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>TryServlet</servlet-name>
        <url-pattern>/tryServlet</url-pattern>
    </servlet-mapping>
</web-app>
<!--
1.<servlet>:
    (1)<servlet-name>指给当前Servlet取名(由程序员手动指定,但一般取类名),该名字唯一。
    (2)<servlet-class>此处为Servlet类的全路径(全类名);Tomcat在反射生成该Servlet时需要使用。
2.<servlet-mapping>:
    (1)<servlet-name>同上
    (2)<url-pattern>是该servlet访问URL的配置(路径)
        当前访问形式:http://localhost:8080/Servlet_Demo/tryServlet
-->

                运行效果(如下GIF图):

编辑

二、 Servlet生命周期

        1.浏览器请求Servlet的流程分析(重要) : 

                 浏览器会先从URL中解析出主机名,eg : www.baidu.com。

                 浏览器到本机C:\Windows\System32\drivers\etc目录下的hosts文件中,去查询有无主机名对应的IP。如下图所示 : 

编辑

                 若本机的hosts文件中没有找到域名对应的IP,就会转向外网的DNS(域名系统)继续查找,如果仍然没有找到,就会提示网站找不到,如下图所示 : 

编辑

                若浏览器在hosts文件中或者在DNS服务器中成功查找到了主机名对应的IP;则会根据获取到的IP,向Tomcat发出HTTP请求

                Tomcat收到HTTP请求后会进行判断——如果是第一次请求,Tomcat会去查询web.xml配置文件,查看请求的资源(eg : /tryServlet) 是否在<servlet-mapping>的url-pattern配置中(即web.xml文件中是否已经配置了该Servlet)。如果找到了对应的url-pattern,就会得到<servlet-name>(eg : TryServlet);如果没有,Tomcat会返回给浏览器一个404页面(找不到资源)。
                Tomcat维护了至少两个牛逼的大的HashMap容器,其中一个是HashMap<id,Servlet>,id以<servlet-name>(不可重复)来存放,Servlet则存放Servlet的实例。Tomcat会通过id查询该HashMap, 确认该Servlet实例(反射创)是否存在
                另一个HashMap容器中, key用来存放web.xml配置文件中的<servlet-mapping>中的<url-pattern>;value则存放<servlet-name>。(所以说,先得到url-pattern才会得到对应的<servlet-name>。
                如果没有查找到<servlet-name>对应的id,即没有该Servlet实例。Tomcat就会根据<servlet-name>来拿到<servlet-class>中的全类名,然后通过反射技术,实例化该Servlet(调用init方法),然后将Servlet实例放入到维护的HashMap集合中。

                 接着,Tomcat调用service方法,由对应的Servlet实例来处理service.对于每次访问请求,Servlet 引擎都会创建一个新的 HttpServletRequest 请求对象和一个新的HttpServletResponse 响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service 方法会根据请求方式,调用doGet | dePost | 其他方法

                6° 如果在HashMap集合中查到了该Servlet实例,就直接调用该Servlet的service方法;可见,Servlet常驻内存,是单例的(仅一个实例)

               servlet实例处理完毕service方法后,会以HttpServletResponse对象的形式将数据返回给Tomcat

                8° Tomcat会对HttpServletResponse对象进行解析,得到数据后再把数据打包到HTTP响应中,以HTTP响应的方式返回给浏览器

                浏览器解析返回的结果并显示。

                PS : 

                浏览器对静态Web资源(html,css,js,图片等)的请求实际是由TomcatDefaultServlet来完成的当其他的utl-pattern都匹配不上时,Tomcat就通过DefaultServlet来拦截到其它静态资源。

        2.生命周期 : 

            1° 初始化阶段

        向Tomcat这样的Servlet 容器,加载 Servlet完成后,Servlet 容器会创建一个 Servlet 实例并调用 init()方法,init()方法只会调用一次(因为Servlet是单例的,一个类只会被加载一次)对每次请求都导致 Servlet 擎调用一次 servlet service 方法

        Servlet 容器在下面的情况装载 Servlet:

        (1)Servlet 容器(Tomcat)启动时自动装载某些 servlet,需要在 web.xml 文件中添加<load-on-startup>1</load-on-startup>,其中1 表示该Servlet装载的顺序
        (2)在 Servlet 容器启动后,浏览器首次发送Servlet 的请求
        (3)Servlet 重新装载后(eg: Tomcat进行 redeploy操作时 [redeploy操作会销毁当前Tomcat维护的HashMap集合中——所有的 Servlet 实例] ),浏览器再次向 Servlet 发送请求的第 1 次,也会导致Servlet的装载。

            2° 处理请求阶段

        1. 处理请求阶段,就是Servlet对service方法的调用。
        2. 每收到一个 HTTP 请求,服务器就会产生一个新的线程去处理该HTTP请求。
        3. 创建一个用于封装 HTTP 请求消息的 ServletRequest 对象和一个代表 HTTP 响应消息的 ServletResponse 对象。
        4. 然后调用 Servlet service()方法并将请求对象和响应对象作为参数进行传递。

            3° 终止阶段

        当web 应用被终止,或者Servlet 容器终止运行,或者Servlet 类重新装载时,会调用 destroy() 方法 , 比如重启 tomcat ,或者 redeploy web 应用。

三、Servlet基本使用

        1.Get请求和Post请求的处理 : 

                以提交一个简单的form表单为例,register.html代码如下 : 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Register</title>
</head>
<body>
    <h1>User Register</h1>
    <form action="http://localhost:8080/Servlet_Demo/tryServlet" method="post">
        username:<input type="text" name="username"/> <br/><br/>
        <input type="submit" value="register"/>
    </form>
</body>
</html>

                在TryServlet类中定义doGet和doPost方法,分别用于处理GET和POST请求,改进后的TryServlet类代码如下 : 

package intro;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;

import java.io.IOException;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
/** 一个类只有实现了Servlet接口,才能被当作Servlet使用。 */
public class TryServlet implements Servlet {

    //创建TryServlet实例时会调用init方法,该方法只会被调用一次。
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("init方法被调用");
    }

    //返回ServletConfig对象,即返回Servlet的配置信息
    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    /*
        (1)service方法用于处理浏览器的请求(包括get/post)
        (2)每当浏览器请求一次Servlet时,都会调用一次service方法。
        (3)当Tomcat调用该方法时,会将请求的数据封装成实现了ServletRequest接口的request对象,
            通过servletRequest对象,可以得到用户提交的数据。
        (4)servletResponse对象可以用于返回数据给Tomcat,Tomcat解析得到对应的数据后,
            会再把数据封装成HTTP响应的方式,发送给浏览器,浏览器再解析并显示。
     */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service is invoked...");

        /*
            ServletRequest类中没有getMethod方法,
            因此要将其转换为它的子接口类型HttpServletRequest.
         */
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String method = httpServletRequest.getMethod();
        System.out.println("method = " + method);

        if ("GET".equals(method)) {
            doGet();
        } else if ("POST".equals(method)) {
            doPost();
        }
    }

    //处理GET请求
    public void doGet() {
        System.out.println("doGet() is invoked");
    }
    //处理POST请求
    public void doPost() {
        System.out.println("dePost() is invoked");
    }

    //返回servlet信息,使用较少
    @Override
    public String getServletInfo() {
        return null;
    }

    //该方法是在servlet被销毁时被Tomcat调用,只调用一次
    @Override
    public void destroy() {
        System.out.println("destroy方法被调用。");
    }
}

                运行效果 : 

编辑

        2.开发Servlet的正确姿势(HttpServlet) : 

                在实际项目中,都是使用继承 HttpServlet 类开发 Servlet 程序,更加便捷。

                HttpServlet类的类图如下 : 

编辑

                HttpServlet直接继承自GenericServlet类(抽象类),而GenericServlet类中实现了Servlet接口的全部五个方法HttpServlet还提供了doGet和doPost方法,并且在实现的service方法中已经做了对method类型的判断;因此,HttpServlet底层走得——其实还是Servlet那一套,只不过人家帮我们封装起来了

                有了HttpServlet后,开发Servlet的步骤如下 —— 

        1. 编写一个类去继承 HttpServlet 类
        2. 根据业务需求重写 doGet 或 doPost 方法
        3. 在web.xml 中配置 Servlet 程序

                以NBServlet为演示类,代码如下 : 

package advance;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class NBServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("NBServlet's doGet is invoked~");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("NBServlet's doPost is invoked~");
    }
}

                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">
    <servlet>
        <servlet-name>TryServlet</servlet-name>
        <servlet-class>intro.TryServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>TryServlet</servlet-name>
        <url-pattern>/tryServlet</url-pattern>
    </servlet-mapping>
    
    <servlet>
        <servlet-name>NBServlet</servlet-name>
        <servlet-class>advance.NBServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>NBServlet</servlet-name>
        <url-pattern>/nBServlet</url-pattern>
    </servlet-mapping>
</web-app>
<!--
1.<servlet>:
    (1)<servlet-name>指给当前Servlet取名(由程序员手动指定,但一般取类名),该名字唯一。
    (2)<servlet-class>此处为Servlet类的全路径(全类名);Tomcat在反射生成该Servlet时需要使用。
2.<servlet-mapping>:
    (1)<servlet-name>同上
    (2)<url-pattern>是该servlet访问URL的配置(路径)———一般采取类名首字母小写的形式.
        当前访问形式:http://localhost:8080/Servlet_Demo/tryServlet
-->

                运行效果(如下GIF图):

编辑

        3.注解方式开发Servlet : 

                使用@WebServlet注解代替web.xml配置文件,eg : @WebServlet(urlPatterns = {"/nbServlet", "/nBServlet"}),可以同时配置多个url,同时生效
                @WebServlet注解类源码如下 : 

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package jakarta.servlet.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
    String name() default "";

    String[] value() default {};

    String[] urlPatterns() default {};

    int loadOnStartup() default -1;

    WebInitParam[] initParams() default {};

    boolean asyncSupported() default false;

    String smallIcon() default "";

    String largeIcon() default "";

    String description() default "";

    String displayName() default "";
}

                若使用@WebServlet的注解方式,首次向Tomcat发送请求时,Tomcat会在IDEA的包下进行扫描,如果发现某个类使用了@WebServlet注解进行修饰,就认为该类是一个Servlet类,继而会读取@WebServlet注解中的urlPattern属性。注意——Tomcat在读取@WebServlet的过程中,就会获取到该Servlet的全类名(全路径,正名)

                其余的步骤与“web.xml”配置的方式大同小异

                以Test类为演示类,代码如下 : 

package advance;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebInitParam;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
@WebServlet(urlPatterns = {"/TTT", "/tee"},
        loadOnStartup = 1,
        initParams = {@WebInitParam(name="n1", value="v1"), 
                @WebInitParam(name = "n2", value = "v2")})
public class Test extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Test's doGet is invoked");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Test's doPost is invoked");
    }
}

                运行效果(如下GIF图):

编辑

        PS :
        WebInitParam注解类源码如下 :

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package jakarta.servlet.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebInitParam {
    String name();

    String value();

    String description() default "";
}

        4.URL匹配的4种方式: 

            1° 精确匹配 

        eg : @WebServlet(urlPatterns={"/TTT", "/tee"})

        精确匹配模式下,只有输入的URL与注解定义的URL完全匹配才能成功找到Servlet资源(推荐)

            2° 目录匹配

        eg : @WebServlet("/TTT/*")

        目录匹配模式下,只要符合/*前面的目录即可,*表示可以后面可以任意写,并且支持多级目录

            3° 扩展名匹配

        eg : @WebServlet("*.action")

        扩展名匹配模式下,前面随便写,只要符合指定的后缀名即可。注意——使用扩展名匹配时前面不能带 / , 否则 tomcat 报错(与带/的匹配模式冲突

            4° 任意匹配

        eg : @WebServlet("/")  or  @WebServlet("/*")

        / /*的配置,会匹配所有的请求(不推荐)

            5° Δ注意事项Δ

        (1) 当Servlet配置了"/",会覆盖掉Tomcat的DefaultServlet,会导致无法访问静态资源。(因为/的优先级比DefaultServlet高,浏览器发出的对静态资源的请求会被当做是对Servlet的请求而匹配);可以到tomcat/conf/web.xml下查看默认配置的DefaultServlet。
        因此,不建议使用任意匹配/ 和 /*
        (2) 4种URL匹配模式的优先级遵守:精确路径 目录路径 扩展名路径 > /* > / > DefaultServlet。

四、Servlet(上)总结

        (1) 了解最基本的“实现Servlet”接口的开发方式;

        (2) 掌握浏览器请求Servlet的流程分析(联系浏览器请求静态资源的流程分析);

        (3) 熟悉Servlet调用的生命周期;

        (4) 掌握“继承HttpServlet类”的开发方式;且明白其底层原理

        (5) 熟悉注解方式开发Servlet的使用,以及与web.xml方式的区别。

        System.out.println("END--------------------------------------------------------------------------------");

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。