目录
思考:我们自己new的Servlet对象受WEB容器的管理吗?
研究:服务器在启动的Servlet对象有没有被创建出来(默认情况下)?
请问:destroy方法调用的时候,对象销毁了还是没有销毁呢?
当我们Servlet类中编写一个有参数的构造方法,如果没有手动编写无参数构造方法会出现什么问题?
思考:Servlet的无参数构造方法是在对象第一次创建的时候执行,并且只执行一次。init方法也是在对象第一次创建的时候执行,并且只执行一次。那么这个无参数构造方法可以代替掉init方法吗?
init、service、destroy方法中使用最多的是哪个方法?
我们编写一个Servlet类直接实现Servlet接口有什么缺点?
编写一个GenericServlet类,这个类是一个抽象类,其中有一个抽象方法service。
思考:GenericServlet类是否需要改造一下?怎么改造?更利于子类程序的编写?
http包下都有哪些类和接口呢?jakarta.servlet.http.*;
我们编写的HelloServlet直接继承HttpServlet,直接重写HttpServlet类中的service()方法行吗?
(老杜的笔记,老杜用的是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表单中指定请求路径即可
-