Servlet
Servlet是一个接口,我们负责开发这个接口的实现类,而像tomcat这样的web服务器负责根据这套接口调用我们开发的实现类的方法来处理WEB请求;
继承关系
interface jakarta.servlet.Servlet
-
唯一子类(实现类):abstract class jakarta.servlet.GenericServlet
-
不推荐直接实现Servlet接口;
-
Servlet中的有一些方法我们用不到,核心的方法其实就只有srevice()而已,对于其他方法我们大多数情况下不会用到,如果直接实现Srevlet接口的话代码非常难看,不简洁优雅,于是就有了GenericServlet。它是一个通用的Servlet,采用了适配器模式设计,将我们不常用的方法(init、destroy……)实现(封装)了,只有一个抽象方法*service()*来让子类实现。
-
源码分析
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable{ private transient ServletConfig config; // 这些方法被GenericServlet实现了(只是空实现),子类如果没有需求的话就不用去实现(重写); public void destroy() { } public String getServletInfo() { return "";} /** * 在init()方法中将WEB容器构造好的Servlet实例赋值给了全局变量config,同时在实现的方法: getServletconfig()当中返回config,这样我们编写的子类可以通过getServletConfig来获取配置信息; * 然后调用了GenericServlet扩展的init()方法,这个扩展的方法是为了让我们有机会在Servlet实例化时编写代码,只需重写这个方法即可; * 因此不建议去重写init(ServletConfig config),否则会导致ServletConfig获取不到,而是建议有需求时重 写init(); */ public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); } public ServletConfig getServletConfig() { return this.config; } public void init() throws ServletException {} }
-
唯一子类:class jakart.servlet.http.HttpServlet
-
主要方法
-
void init(ServletConfig servletConfig)
在Servlet实例化后立即被调用,用于执行一些初始化操作,比如:初始化数据库连接池、线程池……
init方法和构造方法执行时间几乎一致,功能也类似,但是为什么还要有一个init方法呢?
因为编写Servlet类的时候不建议程序员手动添加构造方法,这可能导致在写有参构造的时候忘记添加无参构造从而导致WEB服务器实例化实例化Servlet异常,WEb服务器是通过反射机制以及无参构造来创建Servlet的,所以init方法是有必要存在的;
该方法只会被调用一次,不常用;
-
void service(ServletRequest servletRequest, ServletResponse servletResponse)
Servlet核心方法,当用户发起web请求时,WEB服务器会调用这个方法来处理用户请求;
该方法主要负责:获取请求参数、处理请求、响应结果;
-
void destroy()
Servlet实例被销毁之前会被调用,可以在这里对资源(比如:IO流、数据库连接……)进行释放;
只会被调用一次,不常用;
-
ServletConfig getServletConfig()
-
String getServletInfo()
返回这个Servlet的相关信息:作者、编写时间……
实际开发基本不用;
Servlet的定义
想要定义一个Servlet需要以下步骤:
- 实现
Servlet
接口或者继承其子类:jakarta.servlet.GenericServlet
、jakarta.servlet.http.HttpServlet
; - 如果是实现的Servlet接口:实现接口内部的抽象方法;
- 如果是基于GenericServlet的扩展类:重写service方法;
注册Servlet
定义好的Servlet需要注册才能生效,才能被Tomcat这样的WEB服务器识别;
方式一:web.xml文件里注册
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
一个Servlet标签对应一个Servlet类;
<servlet>
Servlet的别名,便于servlet-mapping标签与当前Servlet进行绑定;
<servlet-name>forwardServlet</servlet-name>
Servlet类的`全限定类名`;
<servlet-class>com.nanum.ForwardServlet</servlet-class>
servlet实例被创建时可以获取这里的初始化参数,K-V形式(可选项);
<init-param>
<param-name>name</param-name>
<param-value>value</param-value>
</init-param>
加上该标签后WEB服务器在启动时以及web应用部署后会 立即 创建Servlet的实例;
数字表示servlet加载的优先级,数值越小的Servlet,越会被WEB服务器优先创建实例;
<!--<load-on-startup>1</load-on-startup>-->
</servlet>
1、servlet映射标签,用于为servlet配置访问路径,即URL;
2、url-pattern标签的值的最前面切记加上`/`以表示从当前项目的根目录开始访问;
3、一个servlet标签至少要有一个servlet-mapping标签与之匹配;
<servlet-mapping>
<servlet-name>forwardServlet</servlet-name>
<url-pattern>/forwardServlet</url-pattern>
</servlet-mapping>
</web-app>
web.xml在项目部署到Tomcat时被Tomcat加载,然后Tomcat根据web.xml文件里配置的Servlet信息,通过反射机制来创建Servlet;
方式二:使用注解注册
冒号里面的内容就是`url-pattern`的值,也就是当前Servlet的访问地址;
也可以传入数组为当前servlet配置多个访问地址:{"/mainServlet","/other"}
@WebServlet("/mainServlet")
public class MainServlet extends HttpServlet {
}
用注解的方式配置servlet是在servlet3.0之后新增的特性,用于简化servlet、Filter、Listener的声明
Servlet的生命周期
当web应用部署到WEB服务器之后,WEB服务器会立即加载并解析web应用中的web.xml,获取Servlet的配置信息,但是默认不会立即实例化Servlet。只有当Servlet被访问的时候WEB服务器才会实例化Servlet;
WEB服务器实例化Servlet是通过Servlet的无参构造函数实现的,所以编写Servlet的时候请务必保证提供无参构造函数;
Servlet只会被WEB服务器创建一个实例,所它是单例的;
-
首先WEB服务器在创建Servlet实例之后会调用
init(ServletConfig servletConfig)
方法来初始化Servlet;通过参数ServletConfig可以获取像我们写在web.xml里的init-param这样的配置信息来完成Servlet的初始化工作;
-
然后这个Servlet实例就在WEB服务器(WEB Server)的内存当中,直到WEB服务器收到Servlet的WEB请求,这时WEB服务器会调用
service()
方法处理请求以及响应结果。每发送一次请求,这个方法就会被调用一次; -
最后Servlet实例里被销毁时调用
destroy()
方法; a)Servlet实例长时间不被访问会被
GC
销毁; b)服务器关闭时会销毁所有Servlet实例;