Servlet | HttpServlet源码分析、web站点的欢迎页面

news2025/7/18 8:19:05

目录

一:HttpServlet源码分析

二:web站点的欢迎页面


一:HttpServlet源码分析

1、HttpServlet类是专门为HTTP协议准备的,比GenericServlet更加适合HTTP协议下的开发

  • HttpServlet在哪个包下?

    • jakarta.servlet.http.HttpServlet

  • 到目前为止接触了servlet规范中哪些接口?

    • jakarta.servlet.Servlet 核心接口(接口)

    • jakarta.servlet.ServletConfig Servlet 配置信息接口(接口)

    • jakarta.servlet.ServletContext Servlet 上下文接口(接口)

    • jakarta.servlet.ServletRequest Servlet请求接口(接口)

    • jakarta.servlet.ServletResponse Servlet响应接口(接口)

    • jakarta.servlet.ServletException Servlet异常(类)

    • jakarta.servlet.GenericServlet 标准通用的Servlet类(抽象类)

  • http包下都有哪些类和接口呢?jakarta.servlet.http.*;

    • jakarta.servlet.http.HttpServlet (HTTP协议专用的Servlet类,抽象类

    • jakarta.servlet.http.HttpServletRequest (HTTP协议专用的请求对象

    • jakarta.servlet.http.HttpServletResponse (HTTP协议专用的响应对象

  • HttpServletRequest对象中封装了什么信息?

    • HttpServletRequest,简称request对象。

    • HttpServletRequest中封装了请求协议(例如:GET请求和POST请求)的全部内容。

    • Tomcat服务器(WEB服务器)将“请求协议”中的数据全部解析出来,然后将这些数据全部封装到request对象当中了。

    • 也就是说,我们只要面向HttpServletRequest,就可以获取请求协议中的数据。

  • HttpServletResponse对象是专门用来响应HTTP协议到浏览器的。

2、 回顾Servlet生命周期 

  • 用户第一次请求

    • Tomcat服务器通过反射机制,调用无参数构造方法。创建Servlet对象。(web.xml文件中配置的Servlet类对应的对象。)

    • Tomcat服务器调用Servlet对象的init()方法完成初始化。

    • Tomcat服务器调用Servlet对象的service()方法处理请求。

  • 用户第二次请求

    • Tomcat服务器调用Servlet对象的service方法处理请求。

  • 用户第N次请求

    • Tomcat服务器调用Servlet对象的service方法处理请求。

  • 服务器关闭

    • Tomcat服务器调用Servlet对象的destroy方法,做销毁之前的准备工作。

    • Tomcat服务器销毁Servlet对象。

3、根据Servlet生命周期进行HttpServlet源码分析

①用户第一次请求,会执行这个无参数构造方法,进行HelloServlet对象的创建

public class HelloServlet extends HttpServlet {
	public HelloServlet() {
    }
    
}

②HelloServlet子类没有提供init()方法,那么必然执行父类HttpServlet的init()方法。HttpServlet类中也没有init()方法,所以会继续执行GenericServlet类中的init()方法。

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    // 用户第一次请求的时候,HelloServlet对象第一次被创建之后,这个init()方法会执行。
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init();
    }
    // 用户第一次请求的时候,带有参数的init(ServletConfig config)执行之后,会执行这个没有参数的    init()
	public void init() throws ServletException {
        // NOOP by default
    }
}

③HelloServlet子类没有提供service()方法。那么必然执行父类HttpServlet类service()方法。

实际上HttpServlet是一个典型的模板类,带有Http的service()方法是一个模板方法

// 实际上HttpServlet是一个典型的模板类,下面带有HTPP参数的service()方法是模板方法。
public abstract class HttpServlet extends GenericServlet {
    // 用户只要发送一次请求,这个service方法就会执行一次。
    @Override
    public void service(ServletRequest req, ServletResponse res) 
    throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
             // 将ServletRequest和ServletResponse向下转型为带有Http的
             // HttpServletRequest和HttpServletResponse
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        } catch (ClassCastException var6) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }
         // 调用重载的service()方法。
        this.service(request, response);
    }
    
    // 下面这些就是重载的service()方法,这个service()方法的参数都是带有HTTP的
    // 这个service()方法是一个模板方法。
    // 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。
    protected void service(HttpServletRequest req, HttpServletResponse resp) 
        throws ServletException, IOException {
        // 调用getMethod()方法获取请求方式:GET POST PUT DELETE HEAD OPTIONS TRACE七种之一
        String method = req.getMethod();
        long lastModified;
        // 如果请求方式是GET请求,则执行doGet方法
        if (method.equals("GET")) {
            lastModified = this.getLastModified(req);
            if (lastModified == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException var9) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < lastModified / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, lastModified);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        // 如果请求方式是HEAD请求,则执行doHead方法。
        } else if (method.equals("HEAD")) {
            lastModified = this.getLastModified(req);
            this.maybeSetLastModified(resp, lastModified);
            this.doHead(req, resp);
        // 如果请求方式是POST请求,则执行doPost方法。
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        // 如果请求方式是PUT请求,则执行doPut方法。
        } else if (method.equals("PUT")) {
            this.doPut(req, resp);
        // 如果请求方式是DELETE请求,则执行doDelete方法。
        } else if (method.equals("DELETE")) {
            this.doDelete(req, resp);
        // 如果请求方式是OPYIONS请求,则执行doOptions方法。
        } else if (method.equals("OPTIONS")) {
            this.doOptions(req, resp);
        // 如果请求方式是TRACE请求,则执行doTrace方法。
        } 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);
        }

    }


}

④我们编写的HelloServlet直接继承HttpServlet,直接重写HttpServlet类中带有Http参数的service()方法也可以,只不过享受不到405错误,享受不到HTTP协议专属的东西!

例:根据发送请求的而方式不同,就调用对应不同的方法,而不是直接重写整个的service()方法;假如前端发送的是GET请求

public class HelloServlet extends HttpServlet {
    // 当前端发送的请求是get请求的时候,我这里重写doGet方法。
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        PrintWriter out = response.getWriter();
        out.print("<h1>doGet</h1>");
    }
    // 当前端发送的请求是post请求的时候,我这里重写doPost方法。
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException{
        PrintWriter out = response.getWriter();
        out.print("<h1>doPost</h1>");
    }
}

编写一个页面,里面有get请求和post请求

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index page</title>
</head>
<body>
    <h1>get请求</h1>
    <a href="/servlet06/hello">hello(get请求)</a><br>

    <h1>post请求</h1>
    <form action="/servlet06/hello" method="post">
        <input type="submit" value="hello">
    </form>
</body>
</html>

假设前端发送了Get请求,但是后端重写的方法是一个doPost()方法就会报405错误,405表示前端的错误,发送的请求方式不对,和服务器不一致,不是服务器需要的请求方式。

 为什么执行405错误,子类并没有重写doGet()方法,实际上就会调用父类HttpServlet的doGet()方法

 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String msg = lStrings.getString("http.method_get_not_supported");
        // 报405错误是因为这一段代码
        this.sendMethodNotAllowed(req, resp, msg);
    }

tips:为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写;这样,确实可以避免405的发生,但是不建议,405错误还是有用的,该报错的时候就应该让他报错。如果是同时重写了doGet和doPost,还不如直接重写service方法,这样代码还能少写一点。

4、Servlet类最终的开发步骤:

第一步:编写一个Servlet类,直接继承HttpServlet

第二步:重写doGet方法或者重写doPost方法,到底重写谁,javaweb程序员说了算。

第三步:将Servlet类配置到web.xml文件当中。

第四步:准备前端的页面(form表单),form表单中指定请求路径即可。

定义一个LoginServlet类继承HttpServlet

package com.bjpowernode.javaweb.servlet;

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 LoginServlet extends HttpServlet {
    // 重写doPost方法
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 根据需求编写代码
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.print("<h1>登录成功...</h1>");
    }
}

编写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>login</servlet-name>
        <servlet-class>com.bjpowernode.javaweb.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>login</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>
</web-app>

编写一个页面表单 ,form表单中指定请求路径即可

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
    <form action="/servlet06/login" method="post">
        <input type="submit" value="login">
    </form>
</body>
</html>

启动Tomcat服务器,进行访问:http://localhost:8080/servlet06/login.html

二:web站点的欢迎页面

什么是一个web站点的欢迎页面?

  • 对于一个webapp来说,是可以设置它的欢迎页面的。设置了欢迎页面之后,当访问这个webapp的时候,或者访问这个web站点的时候,没有指定任何“资源路径”,这个时候会默认访问你的欢迎页面。

  • 一般的访问方式是:

    • http://localhost:8080/servlet06/login.html 指定了要访问的就是login.html资源。

  • 如果访问的方式是:

    • http://localhost:8080/servlet06 如果访问的就是这个站点(不是具体的资源),没有指定具体的资源路径。默认会访问你设置的欢迎页面。

怎么设置整个站点的欢迎页面?

  • 第一步:我在IDEA工具的web目录下新建了一个文件login.html

  • 第二步:在web.xml文件中进行了以下的配置

    • <welcome-file-list>
              <welcome-file>login.html</welcome-file>
      </welcome-file-list>
    • 注意:设置欢迎页面的时候,这个路径不需要以“/”开始,并且这个路径默认是从webapp的根下开始查找。

  • 第三步:启动服务器,浏览器地址栏输入地址

    • http://localhost:8080/servlet07

①在web目录下创建login.html文件 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
    <h1>欢迎登入.....</h1>
</body>
</html>

②配置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>login.html</welcome-file>
    </welcome-file-list>
</web-app>

③启动Tomcat服务器,进行站点的访问

④一个webapp是可以设置多个欢迎页面的 ;越靠上的优先级越高,找不到的继续向下找。 例如:在创建一个具有多级目录(a/b/login.html)的欢迎页面;进行配置

<?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>login.html</welcome-file>
        <welcome-file>a/b/login.html</welcome-file>
    </welcome-file-list>
</web-app>

注意:当文件名设置为index.html的时候,不需要在web.xml文件中进行配置欢迎页面

  • 这是因为小猫咪Tomcat服务器已经提前配置好了。

  • 实际上配置欢迎页面有两个地方可以配置:

    • 一个是在webapp内部的web.xml文件中属于局部配置

    • 一个是在CATALINA_HOME/conf/web.xml文件中进行配置(属于全局配置

      • <welcome-file-list>
            <welcome-file>index.html</welcome-file>
            <welcome-file>index.htm</welcome-file>
            <welcome-file>index.jsp</welcome-file>
        </welcome-file-list>
      • Tomcat服务器的全局欢迎页面是:index.html、index.htm、index.jsp。如果一个web站点没有设置局部的欢迎页面,Tomcat服务器就会以index.html、index.htm、index.jsp作为一个web站点的欢迎页面。

    • 注意原则:局部优先原则(就近原则)。

欢迎页也可以是一个Servlet

欢迎页就是一个资源,既然是一个资源,那么可以是静态资源,也可以是动态资源

        ① 静态资源:index.html welcome.html .....

        ②动态资源:Servlet类

第一步: 写一个Servlet

package com.bjpowernode.javaweb;

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 WelcomeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.print("<h1>welcome to bjpowernode!</h1>");
    }
}

第二步:在web.xml文件中配置servlet

<servlet>
   <servlet-name>welcome</servlet-name>
   <servlet-class>com.bjpowernode.javaweb.WelcomeServlet</servlet-class>
</servlet>
<servlet-mapping>
   <servlet-name>welcome</servlet-name>
   <url-pattern>/welcome</url-pattern>
</servlet-mapping>

第三步:在web.xml文件中配置欢迎页

注意:不需要写项目名且路径不需要以“/”开始

   <welcome-file-list>
        <welcome-file>welcome</welcome-file>
    </welcome-file-list>

补充:关于WEB-INF目录

  • 通常都是在web的根目录下创建html文件,实际上是和WEB-INF目录同级的关系;如果在WEB-INF目录下新建了一个文件:welcome.html

  • 打开浏览器访问:http://localhost:8080/servlet07/WEB-INF/welcome.html 发现出现了404错误。

  • 结论:放在WEB-INF目录下的资源是受保护的。在浏览器上不能够通过路径直接访问。所以像HTML、CSS、JS、image等静态资源一定要放到WEB-INF目录之外。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/8239.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue - - - - vite创建vue3项目(不使用TS)

vite创建vue3项目 vite官方文档 1. 使用指令创建项目 > npm create vite your-project-name > or > yarn create vite your-project-name此处演示使用npm&#xff0c;执行该指令时&#xff0c;遇到下述7.1所示报错。 Need to install the following packages(需…

asp.net+sqlserver婚纱影楼摄影管理系统C#

目录 1绪论 5 1.1 选题背景目的和意义 5 1.2研究现状 5 1.3 课题理由 5 2系统需求分析 7 2.1可行性分析 7 2.1.1 技术可行性 7 2.1.2 经济可行性 7 2.2.3 操作可行性 8 2.2系统架构 8 2.3 业务流程分析 9 3系统总体设计 10 3.1 系统物理环…

JavaSE——异常

目录 一、基本概念 1.1 什么是异常&#xff1f; 1.2 java提供的异常处理机制有什么作用&#xff1f; 1.3 java语言中异常以什么形式存在&#xff1f; 例1&#xff1a; 二、异常处理机制 2.1 所有Exception的直接子类都叫做编译时异常 2.2 所有的RuntimeException及子类都属于…

Spring面试

1. IOC &#xff08;1&#xff09;如何实现一个IOC容器 &#xff08;2&#xff09;IOC理解 &#xff08;3&#xff09;BeanFactory BeanFactory 是 Spring 框架的基础设施&#xff0c;面向 Spring 本身&#xff1b;ApplicationContext 面向使用 Spring 框架的开发者&#xff…

Zookeeper和Eureka的区别

Zookeeper&#xff1a; CP设计(强⼀致性)&#xff0c;⽬标是⼀个分布式的协调系统&#xff0c;⽤于进⾏资源的统⼀管理。当节点crash后&#xff0c;需要进⾏leader的选举&#xff0c;在这个期间内&#xff0c;zk服务是不可⽤的。 eureka&#xff1a; AP设计&#xff08;高可用&…

动态规划--(不同的子序列,编辑距离,两个字符串的删除)

代码随想录day56 动态规划模块 不同的子序列,编辑距离&#xff0c;两个字符串的删除 文章目录1.leetcode 115. 不同的子序列1.1思路及详细步骤1.2 代码示例2.leetcode 583. 两个字符串的删除操作2.1思路及详细步骤2.2 代码示例3.leetcode 72. 编辑距离3.1思路及详细步骤3.2 代码…

绝对最直白的MySQL MVCC机制总结,免费拿走

&#x1f341; 作者&#xff1a;知识浅谈&#xff0c;CSDN签约讲师&#xff0c;后端领域优质创作者&#xff0c;阿里云社区技术博主&#xff0c;热爱分享创作 &#x1f492; 公众号&#xff1a;知识浅谈 &#x1f4cc; 擅长领域&#xff1a;全栈工程师、爬虫、ACM算法 绝对最直…

项目实战 - tpshop商城项目环境搭建

一、环境部署准备 1、软件工具准备 1.1、Vmware虚拟机 1. 在本机上安装好Vmware虚拟机 2. 在虚拟机上安装并运行Linux系统 3. 注意: 实际工具中使用云服务器 1.2、远程连接工具 1. 在本机上安装好远程连接工具 (xshell / putty / FinalShell&#xff08;推荐&#xff09;) …

三种常见的特征选择方法

特征选择 特征选择是特征工程里的一个重要问题&#xff0c;其目标是寻找最优特征子集。特征选择能剔除不相关(irrelevant)或冗余(redundant )的特征&#xff0c;从而达到减少特征个数&#xff0c;提高模型精确度&#xff0c;减少运行时间的目的。并且常能听到“数据和特征决定…

k8s master 实现高可用

Kubernetes高可用master架构 k8s的高可用&#xff0c;主要是实现Master节点的高可用。那么我们看看各个组件是如何解决高可用的。 Kubelet、Kube-proxy&#xff1a;只工作在当前Node节点上&#xff0c;无需高可用。 etcd&#xff1a;etcd如果是放在集群内部的&#xff0c;在…

长文讲解Linux内核性能优化的思路和步骤

一.性能调优简介 1.为什么要进行性能调优&#xff1f; 1&#xff09; 编写的新应用上线前在性能上无法满足需求&#xff0c;这个时候需要对系统进行性能调优 2&#xff09; 应用系统在线上运行后随着系统数据量的不断增长、访问量的不断上升&#xff0c;系统的响应速度通常越…

js-学习链表

链表基础概念 链表和数组一样&#xff0c;可以用于存储一系列连续的元素。链表中的元素在内存中不必是连续的空间。链表的每一个元素有一个由一个存储元素本身的节点和一个指向下一个元素的引用组成(指针和连接)。 链表构成 数据指针 链表优点 1.内存空间不是必须连续的&a…

实践分享:30分钟在电脑端运行小程序

预计实现效果&#xff1a;在电脑桌面端实现小程序运行 技术实现&#xff1a;小程序容器技术实现&#xff08;案例使用FinClip SDK) 技术的原理&#xff1a; 该 SDK 主要包括应用交互层、安全防护、网络通信控制和安全运行容器四个组件。 应用交互层&#xff1a;应用交互层是…

DBSCAN算法实现【超详细注释】

DBSCAN 算法步骤 设置每个对象为未访问 随机选择一个未访问的点ppp,标记ppp表示访问 如果p的半径为nnn的邻域中至少存在MinPts个对象 我们就创建一个新的簇&#xff0c;并将ppp加入ccc设N 是ppp邻域中对象的集合对在NNN中的每个点p′pp′ 如果p′pp′是未访问的 标记p′pp′…

Matlab:数值积分与符号计算

Matlab数值积分定积分的数值求解实现自适应积分算法梯形积分法累计梯形积分多重定积分的数值求解实现符号计算符号对象及其运算符号微积分符号极限符号导数求不定积分符号方程求解数值积分 数值积分&#xff0c;用于求定积分的近似值。在数值分析中&#xff0c;数值积分是计算定…

SQL Server简介

SQL Server是微软的一款关系型数据库。某些平台吹得天花烂坠&#xff0c;今天第一次在自己的项目中使用了下&#xff0c;感觉不是那么好&#xff0c;特别是SQL语句的支持度还是很欠缺&#xff0c;如limit等都不支持&#xff0c;还有特别单双引号都是需要特别注意的,下面是SQL S…

从0到1CTFer成长之路——1git(全网最详细)

1.1.2.1 git 泄露 (1) 常规git泄露 安装docker sudo apt-get update sudo apt-get install docker 安装容器 docker pull ubuntu:18.04 然后发现请求超时 我们需要阿里云的镜像加速器 我们按照要求配置文件 没有/etc/docker 就自己创建 sudo mkdir -p /etc/docker 然后写入…

Cookie与Session 以及给CBV添加装饰器

文章目录Cookie与Session1、Django操作cookie简单实现用户登录加入装饰器2、Django操作session设置session获取session过期时间清除sessionCBV添加装饰器Cookie与Session HTTP被设计为”⽆态”&#xff0c;也就是俗称“脸盲”。 这⼀次请求和下⼀次请求 之间没有任何状态保持&…

跨模态神经搜索实践VCED 环境准备

跨模态神经搜索实践 环境准备 本文基于WSL2及docker进行环境搭建 1. 安装和配置WSL2 Ubuntu发行版 1.1 安装WSL2 Ubuntu 相关安装命令&#xff1a; wsl --install&#xff1a;默认安装Ubuntu发行版wsl --list --online&#xff1a;查看可支持的发行版本wsl --install -d &…

如何将 wordfile 添加到 UltraEdit 或 UEStudio

UltraEdit 本身支持开箱即用的最常用编程和标记语言的语法突出显示。我们也有数百个其他语言的 wordfile&#xff0c;但是&#xff0c;很容易找到和添加您需要的语言&#xff01; 重要提示&#xff1a;此电源提示适用于运行 UltraEdit v15.00或 UEStudio v09.10及更高版本的用户…