1.Servlet的工作过程
当用户通过 URL 发出一个请求时,这些 Java servlet 类就将之转换成一个 HttpServletRequest,并发送给 URL 所指向的目标。当服务器端完成其工作时,Java 运行时环境(JRE)就将结果包装在一个 HttpServletResponse 中,然后将原 HTTP 响应送回给发出该请求的客户机。
在与 Web 应用程序进行交互时,通常会发出多个请求并获得多个响应。所有这些都是在一个会话语境中,Java 语言将之包装在一个 HttpSession 对象中。在处理响应时,您可以访问该对象,并在创建响应时向其添加事件。它提供了一些跨请求的语境。
2.Servlet体系结构
Servlet顶级类关联图
Servlet的框架是由两个Java包组成的:javax.servlet与javax.servlet.http。在javax.servlet包中定义了所有的Servlet类都必须实现或者扩展的通用接口和类。在javax.servlet.http包中定义了采用Http协议通信的HttpServlet类。Servlet的框架的核心是javax.servlet.Servlet接口,所有的Servlet都必须实现这个接口
在该Servlet接口中定义了5个方法:
1. init(ServletConfig)方法:负责初始化Servlet对象,在Servlet的生命周期中,该方法执行一次;该方法执行在单线程的环境下,因此开发者不用考虑线程安全的问题; 2. service(ServletRequest req,ServletResponse res)方法:负责响应客户的请求;为了提高效率,Servlet规范要求一个Servlet实例必须能够同时服务于多个客户端请求,即service()方法运行在多线程的环境下,Servlet开发者必须保证该方法的线程安全性; 3. destroy()方法:当Servlet对象退出生命周期时,负责释放占用的资源; 4. getServletInfo:返回Servlet的描述; 5. getServletConfig:这个方法返回由Servlet容器传给init方法的ServletConfig。
ServletRequest & ServletResponse:
对于每一个HTTP请求,servlet容器会创建一个封装了HTTP请求的ServletRequest实例传递给servlet的service方法,ServletResponse则表示一个Servlet响应,其隐藏了将响应发给浏览器的复杂性。通过ServletRequest的方法你可以获取一些请求相关的参数,而ServletResponse则可以将设置一些返回参数信息,并且设置返回内容
ServletConfig:
ServletConfig封装可以通过@WebServlet或者web.xml传给一个Servlet的配置信息,以这种方式传递的每一条信息都称做初始化信息
ServletContext:
ServletContext是代表了Servlet应用程序。每个Web应用程序只有一个context。在分布式环境中,一个应用程序同时部署到多个容器中,并且每台Java虚拟机都有一个ServletContext对象
GenericServlet:
前面编写的Servlet应用中通过实现Servlet接口来编写Servlet,但是我们每次都必须为Servlet中的所有方法都提供实现,还需要将ServletConfig对象保存到一个类级别的变量中,GenericServlet抽象类就是为了为我们省略一些模板代码,实现了Servlet和ServletConfig
HTTPServlet:
在编写Servlet应用程序时,大多数都要用到HTTP,也就是说可以利用HTTP提供的特性,javax.servlet.http包含了编写Servlet应用程序的类和接口,其中很多覆盖了javax.servlet中的类型,我们自己在编写应用时大多时候也是继承的HttpServlet
3.Servlet工作原理
当Web服务器接收到一个HTTP请求时,它会先判断请求内容——如果是静态网页数据,Web服务器将会自行处理,然后产生响应信息;如果牵涉到动态数据,Web服务器会将请求转交给Servlet容器。此时Servlet容器会找到对应的处理该请求的Servlet实例来处理,结果会送回Web服务器,再由Web服务器传回用户端
针对同一个Servlet,Servlet容器会在第一次收到http请求时建立一个Servlet实例,然后启动一个线程。第二次收到http请求时,Servlet容器无须建立相同的Servlet实例,而是启动第二个线程来服务客户端请求。所以多线程方式不但可以提高Web应用程序的执行效率,也可以降低Web服务器的系统负担
Servlet工作原理时序:🧇
客户端 向Servlet容器(Tomcat)发出Http请求; 2. Servlet容器接收客户端的请求; 3. Servlet容器创建一个HttpRequest对象,将客户端请求的信息封装到这个对象中; 4. Servlet容器创建一个HttpResponse对象; 5. Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象作为参数传给 HttpServlet对象; 6. HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息; 7. HttpServlet调用HttpResponse对象的有关方法,生成响应数据; 8. Servlet容器把HttpServlet的响应结果传给客户端;
4.Servlet生命周期
servlet从出生到死亡的过程就是servlet的生命周期
对应servlet的三个方法:
init()
service()
destroy()
默认情况下:
第一次接受请求时,这个Servlet会进行实例化(构造方法),初始化(init),然后服务(service)
从第二次请求开始,都是服务
关闭tomcat,这个Servlet会被销毁(destroy)
Servlet的生命周期,简单的概括这就分为四步:Servlet类加载--->实例化--->服务--->销毁;
第一次请求时,tomcat才会去实例化,初始化,然后再服务,这样可以提高系统的启动速度😊
如果需要提高系统的启动速度,可以使用默认情况,如果需要提高响应速度,我们应该设置Servlet的初始化时机😶🌫️
Servlet的初始化时机:
我们可以通过来设置Servlet的初始化时机:
例如:在web.xml中
<servlet> <servlet-name>ServletLifeCycle</servlet-name> <servlet-class>servlets.AddServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
的数字越小代表启动优先级越高,启动越靠前,最小值为0
善用可以提高系统的响应速度,升级用户体验🤪但是会导致一定的启动延迟现象
最后,请务必注意:Servlet是单例的,线程不安全的🛍️
当服务器接收到来自客户端的多个请求时,服务器会在单独的Client Service Thread线程中执行Servlet实例的service()方法服务于每个客户端。此时会有多个线程同时执行同一个Servlet实例的service()方法,因此必须考虑线程安全的问题
然而虽然service()方法运行在多线程的环境下,但是却并不一定要同步该方法。而是要看这个方法在执行过程中访问的资源类型及对资源的访问方式。分析如下:
1. 如果service()方法没有访问Servlet的成员变量也没有访问全局的资源比如静态变量、文件、数据库连接等,而是只使用了当前线程自己的资源,比如非指向全局资源的临时变量、request和response对象等。该方法本身就是线程安全的,不必进行任何的同步控制。 2. 如果service()方法访问了Servlet的成员变量,但是对该变量的操作是只读操作,该方法本身就是线程安全的,不必进行任何的同步控制。 3. 如果service()方法访问了Servlet的成员变量,并且对该变量的操作既有读又有写,通常需要加上同步控制语句。 4. 如果service()方法访问了全局的静态变量,如果同一时刻系统中也可能有其它线程访问该静态变量,如果既有读也有写的操作,通常需要加上同步控制语句。 5. 如果service()方法访问了全局的资源,比如文件、数据库连接等,通常需要加上同步控制语句。
Servlet生命周期示例程序:
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Servlet的生命周期 */ public class ServletLifeCycle extends HttpServlet { @Override public void init() throws ServletException { System.out.println("正在初始化..."); } @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("正在服务..."); } @Override public void destroy() { System.out.println("正在销毁..."); } }