关注「Java视界」公众号,获取更多技术干货

servlet相关(流程、原理、生命周期、HttpServlet)

一、什么是servlet ?

Servlet本质上就是一个Java语言定义的接口,广义的Servlet就是指实现了这个Servlet接口的类。一般指的就是后者。

Servlet实例的主要功能就是根据客户端的请求,找到并调用服务端相关java代码,完成对请求的处理和运算。

Servlet实例遵循了服务器能够识别的规则,服务器会根据请求调用对应的servlet进行请求处理。

(1)Servlet实例,由Servlet容器创建,通常这个容器就是tomcat。

(2)Servlet是一个接口:位于javax.servlet包中。

二、Servlet实现步骤

具体实现一个Servlet的步骤如下:

  1. 创建普通的java类,并实现Servlet接口
  2. 重写service方法,添加业务逻辑代码
  3. 在webRoot下的WEB-INF文件夹下的web.xml中配置Servlet及其映射
// 普通Java类实现Servlet接口
public class MyServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    // 添加业务逻辑代码
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("MyServlet 的 service()方法");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

<servlet-name>是指servlet的名称,<servlet>标签和<servlet-mapping>标签中的<servlet-name>必须一致。<servlet-class>是当前servlet的全限定类名,<url-pattern>是映射servlet的url。

三、Servlet生命周期

Servlet生命周期是指servlet实例的创建、初始化、服务执行、销毁的整个过程。

这些流程都是web服务器负责调用执行,程序员无法控制整个流程,但是程序员可以获取到这些Servlet生命周期的时间点,在这些时间点上可以做些业务相关的操作。

理解Servlet的生命周期还要看下Servlet的源码,源码中对其生命周期中的主要节点都有相应的方法:

整个方法调用的流程图如下:

当一个客户端请求过来的时候,tomcat服务器首先解析这个url,首先找到对应的应用app,然后根据/xxx去web.xml中根据配置信息去找对应的servlet,找到对应的servlet后,若还没有创建servlet实例就会新建一个该servlet实例,然后执行init()方法,service()方法,然后响应客户端;若该servlet实例已存在,就只会执行service()方法。当应用卸载的时候,会执行destory()方法销毁这个servlet实例。

我们来演示一下:

public class MyServlet implements Servlet {

    public MyServlet(){
        System.out.println("create servlet !");
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("initialize Servlet !");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("MyServlet's service() method");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("destroy servlet!");
    }
}

浏览器输入地址:

查看日志:

create servlet !
initialize Servlet !
MyServlet's service() method

可以看出执行顺序符合上面流程图定义的顺序,无参构造--》初始化--》service方法,但是没有直接执行destroy销毁方法,一个servlet实例只会在服务停止时调用一次该方法。无参构造和初始化也只会执行一次,若现在再次刷页面,只会执行service方法:

create servlet !
initialize Servlet !
MyServlet's service() method
MyServlet's service() method
MyServlet's service() method

总结一下servlet的特征:

  1. servlet是单例多线程的,单例是指servlet的实例只有一个,多线程是指每次客户端的请求,web服务器都会从线程池中分配一个工作线程去执行servlet的service()方法,线程安全。
  2. 默认情况下,servlet不会在服务启动时实例化,即一个servlet实例的无参构造和初始化也只会执行一次,且不会在服务启动时执行,而是在第一次请求时执行,所以接到请求时会先检查servlet实例是否已经存在
  3. 每次请求只会执行一个service方法
  4. 一个servlet实例只会在服务停止时调用一次该方法

四、HttpServlet

现在我们的请求都是基于HTTP协议的,所以应该专门为HTTP请求写一个Servlet做为通用父类。

HttpServlet的工作流程如下:

  • 1.Web客户向Servlet容器发出Http请求
  • 2.Servlet容器解析Web客户的Http请求
  • 3.Servlet容器创建一个HttpRequest对象,在这个对象中封装Http请求信息
  • 4.Servlet容器创建一个HttpResponse对象
  • 5.Servlet容器调用HttpServlet的service方法,把HttpRequest和HttpResponse对象作为service方法的参数传给HttpServlet对象,service()方法会判断来自客服端的请求方式,根据不同请求方式调用不同方法,如果是get请求,则调用doGet()方法,如果是post请求,则调用doPost()方法
  • 6.HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息
  • 7.HttpServlet调用HttpResponse的有关方法,生成响应数据
  • 8.Servlet容器把HttpServlet的响应结果传给Web客户

补充一:

JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?

JSP (JavaServer Pages)是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达。JSP编译后是"类servlet"。Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。JSP侧重于视图,Servlet主要用于控制逻辑。

补充二:

  • genericservlet类提供了servlet接口的基本实现;
  • HttpServlet是GenericServlet的子类,又是在GenericServlet的基础上做了增强;

补充三:

请求转发与重定向区别

servlet API中forward() 与redirect()的区别? 

前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用 forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用 sendRedirect()方法。从数据共享来说:forward 共享 request 中的数据,redirect 不能。forward - 一般用于用户登陆的时候,根据角色转发到相应的模块, redirect - 一般用于用户注销登陆时返回主页面和跳转到其它的网址等;

补充四:Servlet与线程安全

因为一个类型的Servlet只有一个实例对象,那么就有可能会现时出一个Servlet同时处理多个请求,那么Servlet是否为线程安全的呢?答案是:“不是线程安全的”。这说明Servlet的工作效率很高,但也存在线程安全问题!

所以我们不应该在Servlet中随便创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作。

  • 1.不要在Servlet中创建成员!创建局部变量即可!
  • 2.可以创建无状态成员!无状态表示不可变
  • 3.可以创建有状态的成员,但状态必须为只读的!

posted @ 2022-06-25 14:02  沙滩de流沙  阅读(48)  评论(0编辑  收藏  举报

关注「Java视界」公众号,获取更多技术干货