Servlet对象的生命周期、HttpServlet的继承关系以及HttpServlet源码分析

 

目录

Servlet对象的生命周期

什么是Servlet对象生命周期?

Servlet对象是由谁来维护的?

思考:我们自己new的Servlet对象受WEB容器的管理吗?

 研究:服务器在启动的Servlet对象有没有被创建出来(默认情况下)?

怎么让服务器启动的时候创建Servlet对象呢?

Servlet对象生命周期

请问:destroy方法调用的时候,对象销毁了还是没有销毁呢?

关于Servlet类中方法的调用次数?

当我们Servlet类中编写一个有参数的构造方法,如果没有手动编写无参数构造方法会出现什么问题?

思考:Servlet的无参数构造方法是在对象第一次创建的时候执行,并且只执行一次。init方法也是在对象第一次创建的时候执行,并且只执行一次。那么这个无参数构造方法可以代替掉init方法吗?

 init、service、destroy方法中使用最多的是哪个方法?

GenericServlet

我们编写一个Servlet类直接实现Servlet接口有什么缺点?

适配器设计模式Adapter

编写一个GenericServlet类,这个类是一个抽象类,其中有一个抽象方法service。

思考:GenericServlet类是否需要改造一下?怎么改造?更利于子类程序的编写?

思考一下Tomcat服务器伪代码

ServletConfig

什么是ServletConfig?

ServletConfig接口有哪些常用的方法?

ServletContext

ServletContext接口中有哪些常用的方法?

大家到目前为止都接触过哪些缓存机制了?

堆内存当中的字符串常量池。

堆内存当中的整数型常量池。

连接池(Connection Cache)

线程池

redis

HttpServlet源码分析(重点)

HttpServlet在哪个包下?

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

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

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

回忆Servlet生命周期?

HttpServlet源码分析:

我们编写的HelloServlet直接继承HttpServlet,直接重写HttpServlet类中的service()方法行吗?

我们终于得到了最终的一个Servlet类的开发步骤:


(老杜的笔记,老杜用的是tomcat10,以前的javax变成了jakarta)

Servlet对象的生命周期

什么是Servlet对象生命周期?

  • Servlet对象什么时候被创建。

  • Servlet对象什么时候被销毁。

  • Servlet对象创建了几个?

  • Servlet对象的生命周期表示:一个Servlet对象从出生在最后的死亡,整个过程是怎样的。

Servlet对象是由谁来维护的?

  • Servlet对象的创建,对象上方法的调用,对象最终的销毁,Javaweb程序员是无权干预的。

  • Servlet对象的生命周期是由Tomcat服务器(WEB Server)全权负责的。

  • Tomcat服务器通常我们又称为:WEB容器。(这个叫法你要知道【WEB Container】)

  • WEB容器来管理Servlet对象的死活。

思考:我们自己new的Servlet对象受WEB容器的管理吗?

  • 我们自己new的Servlet对象是不受WEB容器管理的。

  • WEB容器创建的Servlet对象,这些Servlet对象都会被放到一个集合当中(HashMap),只有放到这个HashMap集合中的Servlet才能够被WEB容器管理,自己new的Servlet对象不会被WEB容器管理。(自己new的Servlet对象不在容器当中)

  • web容器底层应该有一个HashMap这样的集合,在这个集合当中存储了Servlet对象和请求路径之间的关系

 研究:服务器在启动的Servlet对象有没有被创建出来(默认情况下)?

  • 在Servlet中提供一个无参数的构造方法,启动服务器的时候看看构造方法是否执行。

  • 经过测试得出结论:默认情况下,服务器在启动的时候Servlet对象并不会被实例化。

  • 这个设计是合理的。用户没有发送请求之前,如果提前创建出来所有的Servlet对象,必然是耗费内存的,并且创建出来的Servlet如果一直没有用户访问,显然这个Servlet对象是一个废物,没必要先创建。

怎么让服务器启动的时候创建Servlet对象呢?

在servlet标签中添加<load-on-startup>子标签,在该子标签中填写整数,越小的整数优先级越高。

<servlet>
    <servlet-name>aservlet</servlet-name>
    <servlet-class>com.bjpowernode.javaweb.servlet.AServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>aservlet</servlet-name>
    <url-pattern>/a</url-pattern>
</servlet-mapping>

Servlet对象生命周期

  • 默认情况下服务器启动的时候AServlet对象并没有被实例化

  • 用户发送第一次请求的时候,控制台输出了以下内容:

AServlet无参数构造方法执行了
AServlet's init method execute!
AServlet's service method execute!
  • 根据以上输出内容得出结论:

    • 用户在发送第一次请求的时候Servlet对象被实例化(AServlet的构造方法被执行了。并且执行的是无参数构造方法。)

    • AServlet对象被创建出来之后,Tomcat服务器马上调用了AServlet对象的init方法。(init方法在执行的时候,AServlet对象已经存在了。已经被创建出来了。)

    • 用户发送第一次请求的时候,init方法执行之后,Tomcat服务器马上调用AServlet对象的service方法。

  • 用户继续发送第二次请求,控制台输出了以下内容:

AServlet's service method execute!
  • 根据以上输出结果得知,用户在发送第二次,或者第三次,或者第四次请求的时候,Servlet对象并没有新建,还是使用之前创建好的Servlet对象,直接调用该Servlet对象的service方法,这说明:

    • 第一:Servlet对象是单例的(单实例的。但是要注意:Servlet对象是单实例的,但是Servlet类并不符合单例模式。我们称之为假单例。之所以单例是因为Servlet对象的创建我们javaweb程序员管不着,这个对象的创建只能是Tomcat来说了算,Tomcat只创建了一个,所以导致了单例,但是属于假单例。真单例模式,构造方法是私有化的。)

    • 第二:无参数构造方法、init方法只在第一次用户发送请求的时候执行。也就是说无参数构造方法只执行一次。init方法也只被Tomcat服务器调用一次。

    • 第三:只要用户发送一次请求:service方法必然会被Tomcat服务器调用一次。发送100次请求,service方法会被调用100次。

  • 关闭服务器的时候,控制台输出了以下内容:

AServlet's destroy method execute!

通过以上输出内容,可以得出以下结论:

  • Servlet的destroy方法只被Tomcat服务器调用一次。

  • destroy方法是在什么时候被调用的?

    • 在服务器关闭的时候。

    • 因为服务器关闭的时候要销毁AServlet对象的内存。

    • 服务器在销毁AServlet对象内存之前,Tomcat服务器会自动调用AServlet对象的destroy方法。

请问:destroy方法调用的时候,对象销毁了还是没有销毁呢?

destroy方法执行的时候AServlet对象还在,没有被销毁。destroy方法执行结束之后,AServlet对象的内存才会被Tomcat释放。

关于Servlet类中方法的调用次数?

  • 构造方法只执行一次。

  • init方法只执行一次。

  • service方法:用户发送一次请求则执行一次,发送N次请求则执行N次。

  • destroy方法只执行一次。

当我们Servlet类中编写一个有参数的构造方法,如果没有手动编写无参数构造方法会出现什么问题?

  • 报错了:500错误。

  • 注意:500是一个HTTP协议的错误状态码。

  • 500一般情况下是因为服务器端的Java程序出现了异常。(服务器端的错误都是500错误:服务器内部错误。)

  • 如果没有无参数的构造方法,会导致出现500错误,无法实例化Servlet对象。

  • 所以,一定要注意:在Servlet开发当中,不建议程序员来定义构造方法,因为定义不当,一不小心就会导致无法实例化Servlet对象。

 

思考:Servlet的无参数构造方法是在对象第一次创建的时候执行,并且只执行一次。init方法也是在对象第一次创建的时候执行,并且只执行一次。那么这个无参数构造方法可以代替掉init方法吗?

  • 不能。

  • Servlet规范中有要求,作为javaweb程序员,编写Servlet类的时候,不建议手动编写构造方法,因为编写构造方法,很容易让无参数构造方法消失,这个操作可能会导致Servlet对象无法实例化。所以init方法是有存在的必要的。

 init、service、destroy方法中使用最多的是哪个方法?

  • 使用最多就是service方法,service方法是一定要实现的,因为service方法是处理用户请求的核心方法。

  • 什么时候使用init方法呢?

    • init方法很少用。

    • 通常在init方法当中做初始化操作,并且这个初始化操作只需要执行一次。例如:初始化数据库连接池,初始化线程池....

  • 什么时候使用destroy方法呢?

    • destroy方法也很少用。

    • 通常在destroy方法当中,进行资源的关闭。马上对象要被销毁了,还有什么没有关闭的,抓紧时间关闭资源。还有什么资源没保存的,抓紧时间保存一下。

GenericServlet

我们编写一个Servlet类直接实现Servlet接口有什么缺点?

我们只需要service方法,其他方法大部分情况下是不需要使用的。代码很丑陋。

适配器设计模式Adapter

手机直接插到220V的电压上,手机直接就报废了。怎么办?可以找一个充电器。这个充电器就是一个适配器。手机连接适配器。适配器连接220V的电压。这样问题就解决了。

编写一个GenericServlet类,这个类是一个抽象类,其中有一个抽象方法service。

  • GenericServlet实现Servlet接口。

  • GenericServlet是一个适配器。

  • 以后编写的所有Servlet类继承GenericServlet,重写service方法即可。

思考:GenericServlet类是否需要改造一下?怎么改造?更利于子类程序的编写?

  • 思考第一个问题:我提供了一个GenericServlet之后,init方法还会执行吗?

    • 还会执行。会执行GenericServlet类中的init方法。

  • 思考第二个问题:init方法是谁调用的?

    • Tomcat服务器调用的。

  • 思考第三个问题:init方法中的ServletConfig对象是谁创建的?是谁传过来的?

    • 都是Tomcat干的。

    • Tomcat服务器先创建了ServletConfig对象,然后调用init方法,将ServletConfig对象传给了init方法。

  • 思考一下Tomcat服务器伪代码

    public class Tomcat {
        public static void main(String[] args){
            // .....
            // Tomcat服务器伪代码
            // 创建LoginServlet对象(通过反射机制,调用无参数构造方法来实例化LoginServlet对象)
            Class clazz = Class.forName("com.bjpowernode.javaweb.servlet.LoginServlet");
            Object obj = clazz.newInstance();
            
            // 向下转型
            Servlet servlet = (Servlet)obj;
            
            // 创建ServletConfig对象
            // Tomcat服务器负责将ServletConfig对象实例化出来。
            // 多态(Tomcat服务器完全实现了Servlet规范)
            ServletConfig servletConfig = new org.apache.catalina.core.StandardWrapperFacade();
            
            // 调用Servlet的init方法
            servlet.init(servletConfig);
            
            // 调用Servlet的service方法
            // ....
            
        }
    }

    ServletConfig

  • 什么是ServletConfig?

    • Servlet对象的配置信息对象。

    • ServletConfig对象中封装了<servlet></servlet>标签中的配置信息。(web.xml文件中servlet的配置信息)

  • 一个Servlet对应一个ServletConfig对象。

  • Servlet对象是Tomcat服务器创建,并且ServletConfig对象也是Tomcat服务器创建。并且默认情况下,他们都是在用户发送第一次请求的时候创建。

  • Tomcat服务器调用Servlet对象的init方法的时候需要传一个ServletConfig对象的参数给init方法。

  • ServletConfig接口的实现类是Tomcat服务器给实现的。(Tomcat服务器说的就是WEB服务器。)

  • ServletConfig接口有哪些常用的方法?

    public String getInitParameter(String name); // 通过初始化参数的name获取value
    public Enumeration<String> getInitParameterNames(); // 获取所有的初始化参数的name
    public ServletContext getServletContext(); // 获取ServletContext对象
    public String getServletName(); // 获取Servlet的name
  • 以上方法在Servlet类当中,都可以使用this去调用。因为GenericServlet实现了ServletConfig接口

  • ServletContext

  • 一个Servlet对象对应一个ServletConfig。100个Servlet对象则对应100个ServletConfig对象。

  • 只要在同一个webapp当中,只要在同一个应用当中,所有的Servlet对象都是共享同一个ServletContext对象的。

  • ServletContext对象在服务器启动阶段创建,在服务器关闭的时候销毁。这就是ServletContext对象的生命周期。ServletContext对象是应用级对象。

  • Tomcat服务器中有一个webapps,这个webapps下可以存放webapp,可以存放多个webapp,假设有100个webapp,那么就有100个ServletContext对象。但是,总之,一个应用,一个webapp肯定是只有一个ServletContext对象。

  • ServletContext被称为Servlet上下文对象。(Servlet对象的四周环境对象。)

  • 一个ServletContext对象通常对应的是一个web.xml文件。

  • ServletContext对应显示生活中的什么例子呢?

    • 一个教室里有多个学生,那么每一个学生就是一个Servlet,这些学生都在同一个教室当中,那么我们可以把这个教室叫做ServletContext对象。那么也就是说放在这个ServletContext对象(环境)当中的数据,在同一个教室当中,物品都是共享的。比如:教室中有一个空调,所有的学生都可以操作。可见,空调是共享的。因为空调放在教室当中。教室就是ServletContext对象。

  • ServletContext是一个接口,Tomcat服务器对ServletContext接口进行了实现。

    • ServletContext对象的创建也是Tomcat服务器来完成的。启动webapp的时候创建的。

  • ServletContext接口中有哪些常用的方法?

    public String getInitParameter(String name); // 通过初始化参数的name获取value
    public Enumeration<String> getInitParameterNames(); // 获取所有的初始化参数的name
    <!--以上两个方法是ServletContext对象的方法,这个方法获取的是什么信息?是以下的配置信息-->
    <context-param>
        <param-name>pageSize</param-name>
        <param-value>10</param-value>
    </context-param>
    <context-param>
        <param-name>startIndex</param-name>
        <param-value>0</param-value>
    </context-param>
    <!--注意:以上的配置信息属于应用级的配置信息,一般一个项目中共享的配置信息会放到以上的标签当中。-->
    <!--如果你的配置信息只是想给某一个servlet作为参考,那么你配置到servlet标签当中即可,使用ServletConfig对象来获取。-->
    // 获取应用的根路径(非常重要),因为在java源代码当中有一些地方可能会需要应用的根路径,这个方法可以动态获取应用的根路径
    // 在java源码当中,不要将应用的根路径写死,因为你永远都不知道这个应用在最终部署的时候,起一个什么名字。
    public String getContextPath();
    //String contextPath = application.getContextPath();
    // 获取文件的绝对路径(真实路径)
    public String getRealPath(String path);
    // 通过ServletContext对象也是可以记录日志的
    public void log(String message);
    public void log(String message, Throwable t);
    // 这些日志信息记录到哪里了?
    // localhost.2021-11-05.log
    
    // Tomcat服务器的logs目录下都有哪些日志文件?
    //catalina.2021-11-05.log 服务器端的java程序运行的控制台信息。
    //localhost.2021-11-05.log ServletContext对象的log方法记录的日志信息存储到这个文件中。
    //localhost_access_log.2021-11-05.txt 访问日志
    // ServletContext对象还有另一个名字:应用域(后面还有其他域,例如:请求域、会话域)
    
    // 如果所有的用户共享一份数据,并且这个数据很少的被修改,并且这个数据量很少,可以将这些数据放到ServletContext这个应用域中
    
    // 为什么是所有用户共享的数据? 不是共享的没有意义。因为ServletContext这个对象只有一个。只有共享的数据放进去才有意义。
    
    // 为什么数据量要小? 因为数据量比较大的话,太占用堆内存,并且这个对象的生命周期比较长,服务器关闭的时候,这个对象才会被销毁。大数据量会影响服务器的性能。占用内存较小的数据量可以考虑放进去。
    
    // 为什么这些共享数据很少的修改,或者说几乎不修改?
    // 所有用户共享的数据,如果涉及到修改操作,必然会存在线程并发所带来的安全问题。所以放在ServletContext对象中的数据一般都是只读的。
    
    // 数据量小、所有用户共享、又不修改,这样的数据放到ServletContext这个应用域当中,会大大提升效率。因为应用域相当于一个缓存,放到缓存中的数据,下次在用的时候,不需要从数据库中再次获取,大大提升执行效率。
    
    // 存(怎么向ServletContext应用域中存数据)
    public void setAttribute(String name, Object value); // map.put(k, v)
    // 取(怎么从ServletContext应用域中取数据)
    public Object getAttribute(String name); // Object v = map.get(k)
    // 删(怎么删除ServletContext应用域中的数据)
    public void removeAttribute(String name); // map.remove(k)

    注意:以后我们编写Servlet类的时候,实际上是不会去直接继承GenericServlet类的,因为我们是B/S结构的系统,这种系统是基于HTTP超文本传输协议的,在Servlet规范当中,提供了一个类叫做HttpServlet,它是专门为HTTP协议准备的一个Servlet类。我们编写的Servlet类要继承HttpServlet。(HttpServlet是HTTP协议专用的。)使用HttpServlet处理HTTP协议更便捷。但是你需要直到它的继承结构:

  • jakarta.servlet.Servlet(接口)【爷爷】
    jakarta.servlet.GenericServlet implements Servlet(抽象类)【儿子】
    jakarta.servlet.http.HttpServlet extends GenericServlet(抽象类)【孙子】
    
    我们以后编写的Servlet要继承HttpServlet类。

    大家到目前为止都接触过哪些缓存机制了?

  • 堆内存当中的字符串常量池。

    • "abc" 先在字符串常量池中查找,如果有,直接拿来用。如果没有则新建,然后再放入字符串常量池。

  • 堆内存当中的整数型常量池。

    • [-128 ~ 127] 一共256个Integer类型的引用,放在整数型常量池中。没有超出这个范围的话,直接从常量池中取。

  • 连接池(Connection Cache)

    • 这里所说的连接池中的连接是java语言连接数据库的连接对象:java.sql.Connection对象。

    • JVM是一个进程。MySQL数据库是一个进程。进程和进程之间建立连接,打开通道是很费劲的。是很耗费资源的。怎么办?可以提前先创建好N个Connection连接对象,将连接对象放到一个集合当中,我们把这个放有Connection对象的集合称为连接池。每一次用户连接的时候不需要再新建连接对象,省去了新建的环节,直接从连接池中获取连接对象,大大提升访问效率。

    • 连接池

      • 最小连接数

      • 最大连接数

      • 连接池可以提高用户的访问效率。当然也可以保证数据库的安全性。

  • 线程池

    • Tomcat服务器本身就是支持多线程的。

    • Tomcat服务器是在用户发送一次请求,就新建一个Thread线程对象吗?

      • 当然不是,实际上是在Tomcat服务器启动的时候,会先创建好N多个线程Thread对象,然后将线程对象放到集合当中,称为线程池。用户发送请求过来之后,需要有一个对应的线程来处理这个请求,这个时候线程对象就会直接从线程池中拿,效率比较高。

      • 所有的WEB服务器,或者应用服务器,都是支持多线程的,都有线程池机制。

  • redis

    • NoSQL数据库。非关系型数据库。缓存数据库。

  • 向ServletContext应用域中存储数据,也等于是将数据存放到缓存cache当中了。

HttpServlet源码分析(重点)

  • 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中封装了请求协议的全部内容。

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

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

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

  • 回忆Servlet生命周期?

    • 用户第一次请求

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

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

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

    • 用户第二次请求

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

    • 用户第三次请求

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

    • ....

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

    • 服务器关闭

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

      • Tomcat服务器销毁Servlet对象。

  • HttpServlet源码分析:

public class HelloServlet extends HttpServlet {
	// 用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法。
	public HelloServlet() {
    }
    
    //override 重写 doGet方法
    //override 重写 doPost方法
}

public abstract class GenericServlet implements Servlet, ServletConfig,
        java.io.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
    }
}

// HttpServlet模板类。
public abstract class HttpServlet extends GenericServlet {
    // 用户发送第一次请求的时候这个service会执行
    // 用户发送第N次请求的时候,这个service方法还是会执行。
    // 用户只要发送一次请求,这个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 e) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }
        // 调用重载的service方法。
        service(request, response);
    }
    
    // 这个service方法的两个参数都是带有Http的。
    // 这个service是一个模板方法。
    // 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        // 获取请求方式
        // 这个请求方式最终可能是:""
        // 注意:request.getMethod()方法获取的是请求方式,可能是七种之一:
        // GET POST PUT DELETE HEAD OPTIONS TRACE
        String method = req.getMethod();

        // 如果请求方式是GET请求,则执行doGet方法。
        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                } catch (IllegalArgumentException iae) {
                    // Invalid date header - proceed as if none was set
                    ifModifiedSince = -1;
                }
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            // 如果请求方式是POST请求,则执行doPost方法。
            doPost(req, resp);

        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);

        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);

        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);

        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);

        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);

            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
    
    
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException{
        // 报405错误
        String msg = lStrings.getString("http.method_get_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }
    
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        // 报405错误
        String msg = lStrings.getString("http.method_post_not_supported");
        sendMethodNotAllowed(req, resp, msg);
    }
    
}

/*
通过以上源代码分析:
	假设前端发送的请求是get请求,后端程序员重写的方法是doPost
	假设前端发送的请求是post请求,后端程序员重写的方法是doGet
	会发生什么呢?
		发生405这样的一个错误。
		405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。
	
	通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405.

怎么避免405的错误呢?
	后端重写了doGet方法,前端一定要发get请求。
	后端重写了doPost方法,前端一定要发post请求。
	这样可以避免405错误。
	
	这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。
	
有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。
这样,确实可以避免405的发生,但是不建议,405错误还是有用的。该报错的时候就应该让他报错。
如果你要是同时重写了doGet和doPost,那还不如你直接重写service方法好了。这样代码还能
少写一点。
*/

  • 我们编写的HelloServlet直接继承HttpServlet,直接重写HttpServlet类中的service()方法行吗?

    • 可以,只不过你享受不到405错误。享受不到HTTP协议专属的东西。

  • 我们终于得到了最终的一个Servlet类的开发步骤:

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

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

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

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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java塑造中...

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值