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


    目录

    一: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对象的创建

    1. public class HelloServlet extends HttpServlet {
    2. public HelloServlet() {
    3. }
    4. }

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

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

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

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

    1. // 实际上HttpServlet是一个典型的模板类,下面带有HTPP参数的service()方法是模板方法。
    2. public abstract class HttpServlet extends GenericServlet {
    3. // 用户只要发送一次请求,这个service方法就会执行一次。
    4. @Override
    5. public void service(ServletRequest req, ServletResponse res)
    6. throws ServletException, IOException {
    7. HttpServletRequest request;
    8. HttpServletResponse response;
    9. try {
    10. // 将ServletRequest和ServletResponse向下转型为带有Http的
    11. // HttpServletRequest和HttpServletResponse
    12. request = (HttpServletRequest)req;
    13. response = (HttpServletResponse)res;
    14. } catch (ClassCastException var6) {
    15. throw new ServletException(lStrings.getString("http.non_http"));
    16. }
    17. // 调用重载的service()方法。
    18. this.service(request, response);
    19. }
    20. // 下面这些就是重载的service()方法,这个service()方法的参数都是带有HTTP的
    21. // 这个service()方法是一个模板方法。
    22. // 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。
    23. protected void service(HttpServletRequest req, HttpServletResponse resp)
    24. throws ServletException, IOException {
    25. // 调用getMethod()方法获取请求方式:GET POST PUT DELETE HEAD OPTIONS TRACE七种之一
    26. String method = req.getMethod();
    27. long lastModified;
    28. // 如果请求方式是GET请求,则执行doGet方法
    29. if (method.equals("GET")) {
    30. lastModified = this.getLastModified(req);
    31. if (lastModified == -1L) {
    32. this.doGet(req, resp);
    33. } else {
    34. long ifModifiedSince;
    35. try {
    36. ifModifiedSince = req.getDateHeader("If-Modified-Since");
    37. } catch (IllegalArgumentException var9) {
    38. ifModifiedSince = -1L;
    39. }
    40. if (ifModifiedSince < lastModified / 1000L * 1000L) {
    41. this.maybeSetLastModified(resp, lastModified);
    42. this.doGet(req, resp);
    43. } else {
    44. resp.setStatus(304);
    45. }
    46. }
    47. // 如果请求方式是HEAD请求,则执行doHead方法。
    48. } else if (method.equals("HEAD")) {
    49. lastModified = this.getLastModified(req);
    50. this.maybeSetLastModified(resp, lastModified);
    51. this.doHead(req, resp);
    52. // 如果请求方式是POST请求,则执行doPost方法。
    53. } else if (method.equals("POST")) {
    54. this.doPost(req, resp);
    55. // 如果请求方式是PUT请求,则执行doPut方法。
    56. } else if (method.equals("PUT")) {
    57. this.doPut(req, resp);
    58. // 如果请求方式是DELETE请求,则执行doDelete方法。
    59. } else if (method.equals("DELETE")) {
    60. this.doDelete(req, resp);
    61. // 如果请求方式是OPYIONS请求,则执行doOptions方法。
    62. } else if (method.equals("OPTIONS")) {
    63. this.doOptions(req, resp);
    64. // 如果请求方式是TRACE请求,则执行doTrace方法。
    65. } else if (method.equals("TRACE")) {
    66. this.doTrace(req, resp);
    67. } else {
    68. String errMsg = lStrings.getString("http.method_not_implemented");
    69. Object[] errArgs = new Object[]{method};
    70. errMsg = MessageFormat.format(errMsg, errArgs);
    71. resp.sendError(501, errMsg);
    72. }
    73. }
    74. }

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

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

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

      doGet

      "
      );
    7. }
    8. // 当前端发送的请求是post请求的时候,我这里重写doPost方法。
    9. protected void doPost(HttpServletRequest request, HttpServletResponse response)
    10. throws ServletException, IOException{
    11. PrintWriter out = response.getWriter();
    12. out.print("

      doPost

      "
      );
    13. }
    14. }

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

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>index pagetitle>
    6. head>
    7. <body>
    8. <h1>get请求h1>
    9. <a href="/servlet06/hello">hello(get请求)a><br>
    10. <h1>post请求h1>
    11. <form action="/servlet06/hello" method="post">
    12. <input type="submit" value="hello">
    13. form>
    14. body>
    15. html>

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

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

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

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

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

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

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

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

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

    定义一个LoginServlet类继承HttpServlet

    1. package com.bjpowernode.javaweb.servlet;
    2. import javax.servlet.ServletException;
    3. import javax.servlet.http.HttpServlet;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import java.io.IOException;
    7. import java.io.PrintWriter;
    8. public class LoginServlet extends HttpServlet {
    9. // 重写doPost方法
    10. @Override
    11. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    12. // 根据需求编写代码
    13. response.setContentType("text/html");
    14. PrintWriter out = response.getWriter();
    15. out.print("

      登录成功...

      "
      );
    16. }
    17. }

    编写web.xml配置文件

    1. "1.0" encoding="UTF-8"?>
    2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    5. version="4.0">
    6. <servlet>
    7. <servlet-name>loginservlet-name>
    8. <servlet-class>com.bjpowernode.javaweb.servlet.LoginServletservlet-class>
    9. servlet>
    10. <servlet-mapping>
    11. <servlet-name>loginservlet-name>
    12. <url-pattern>/loginurl-pattern>
    13. servlet-mapping>
    14. web-app>

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

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>logintitle>
    6. head>
    7. <body>
    8. <form action="/servlet06/login" method="post">
    9. <input type="submit" value="login">
    10. form>
    11. body>
    12. html>

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

    二:web站点的欢迎页面

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

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

    • 一般的访问方式是:

    • 如果访问的方式是:

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

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

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

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

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

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

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

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>logintitle>
    6. head>
    7. <body>
    8. <h1>欢迎登入.....h1>
    9. body>
    10. html>

    ②配置web.xml文件

    1. "1.0" encoding="UTF-8"?>
    2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    5. version="4.0">
    6. <welcome-file-list>
    7. <welcome-file>login.htmlwelcome-file>
    8. welcome-file-list>
    9. web-app>

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

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

    1. "1.0" encoding="UTF-8"?>
    2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    5. version="4.0">
    6. <welcome-file-list>
    7. <welcome-file>login.htmlwelcome-file>
    8. <welcome-file>a/b/login.htmlwelcome-file>
    9. welcome-file-list>
    10. web-app>

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

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

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

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

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

          1. <welcome-file-list>
          2.    <welcome-file>index.htmlwelcome-file>
          3.    <welcome-file>index.htmwelcome-file>
          4.    <welcome-file>index.jspwelcome-file>
          5. 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

    1. package com.bjpowernode.javaweb;
    2. import javax.servlet.ServletException;
    3. import javax.servlet.http.HttpServlet;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. import java.io.IOException;
    7. import java.io.PrintWriter;
    8. public class WelcomeServlet extends HttpServlet {
    9. @Override
    10. protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    11. response.setContentType("text/html");
    12. PrintWriter out = response.getWriter();
    13. out.print("

      welcome to bjpowernode!

      "
      );
    14. }
    15. }

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

    1. <servlet>
    2. <servlet-name>welcomeservlet-name>
    3. <servlet-class>com.bjpowernode.javaweb.WelcomeServletservlet-class>
    4. servlet>
    5. <servlet-mapping>
    6. <servlet-name>welcomeservlet-name>
    7. <url-pattern>/welcomeurl-pattern>
    8. servlet-mapping>

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

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

    1. <welcome-file-list>
    2. <welcome-file>welcomewelcome-file>
    3. 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目录之外。

  • 相关阅读:
    网路了解篇-1
    v73.结构
    第6章:数据库设计基础知识
    SpringCloud Alibaba(二) - Sentinel,整合OpenFeign,GateWay服务网关
    window 添加/删除系统右键菜单项
    用 Python 写的摸鱼监控进程,千万别让老板知道
    大数据平台搭建及集群规划
    索引常见面试题
    面经-常用框架
    文件包含漏洞
  • 原文地址:https://blog.csdn.net/m0_61933976/article/details/127715361