Servlet的基础详解与架构解析

Servlet快速入门

什么是Servlet?

Servlet是基于Java技术的Web组件,由容器管理并产生动态的内容。Servlet与客户端通过Servlet容器实现的请求/响应模型进行交互。

SpringMVC框架的底层是基于Servlet实现的。

入门代码

1.在我们的项目中创建libs目录存放第三方的jar包

2.项目中导入servlet-api.jar libs目录中,就在我们tomcat安装的目录 中 lib 目录中(也可以直接使用Maven,就不用这么麻烦了)

3.创建servlet包,专门存放就是我们的servlet

4.创建IndexServlet,实现Servlet 重写方法

5.IndexServlet 类上加上@WebServlet("/Hello"),注解定义 URL访问的路径

6.重写Servlet 类中service 在service中编写 动态资源

Tomcat的项目配置

servlet架构 servlet基本架构代码_java

IndexServlet类的详细代码

@WebServlet("/Hello")
public class IndexServlet 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 {
        String username = servletRequest.getParameter("username");
        servletResponse.setContentType("Text/html;charset=utf-8");
        PrintWriter writer = servletResponse.getWriter();
        if("Harmony".equals(username)) {
            writer.println("可以访问");
        } else {
            writer.println("拒绝访问");
        }
        writer.close();
    }

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

    @Override
    public void destroy() {

    }
}

debug结果如下 

servlet架构 servlet基本架构代码_架构_02

 

servlet架构 servlet基本架构代码_servlet架构_03

@WebServlet注解 

@WebServlet 用于将一个类声明为 Servlet,该注解会在部署时被容器处理,容器根据其具体的属性配置将相应的类部署为 Servlet。该注解具有下表给出的一些常用属性。

属性名

类型

标签

描述

是否必需

name

String

<servlet-name>

指定 Servlet 的 name 属性。

如果没有显式指定,则取值为该 Servlet 的完全限定名,即包名+类名。


value

String[ ]

<url-pattern>

该属性等价于 urlPatterns 属性,两者不能同时指定。

如果同时指定,通常是忽略 value 的取值。


urlPatterns

String[ ]

<url-pattern>

指定一组 Servlet 的 URL 匹配模式。


loadOnStartup

int

<load-on-startup>

指定 Servlet 的加载顺序。


initParams

WebInitParam[ ]

<init-param>

指定一组 Servlet 初始化参数。


asyncSupported

boolean

<async-supported>

声明 Servlet 是否支持异步操作模式。


description

String

<description>

指定该 Servlet 的描述信息。


displayName

String

<display-name>

指定该 Servlet 的显示名。


注意: 

  • 通过实现 Serlvet 接口或继承 GenericServlet 创建的 Servlet 类无法使用 @WebServlet 注解。

@WebServlet 注解 和 web.xml 的优缺点

使用 web.xml 或 @WebServlet 注解都可以配置 Servlet, 两者各有优缺点。

@WebServlet 注解配置 Servlet

  • 优点:@WebServlet 直接在 Servlet 类中使用,代码量少,配置简单。每个类只关注自身业务逻辑,与其他 Servlet 类互不干扰,适合多人同时开发。
  • 缺点:Servlet 较多时,每个 Servlet 的配置分布在各自的类中,不便于查找和修改。

web.xml 配置文件配置 Servlet

  • 优点:集中管理 Servlet 的配置,便于查找和修改。
  • 缺点:代码较繁琐,可读性不强,不易于理解。

servlet 执行流程

问:servlet是谁创建的?service是谁在调用?

servlet是由我们的 web服务器(tomcat)创建、该方法是由我们的 web服务器(tomcat)调用

问:tomcat服务器如何执行到servlet中的service方法?

我们创建的servlet实现Servlet接口,重写了service方法。

所以总的来说,Servlet的执行流程:

客户端发起HTTP请求,请求到达Tomcat服务器,通过URL映射找到所对应的、具体的Servlet,再调用其service方法。

servlet 生命周期(重点)

Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:

  • Servlet 初始化后调用 init () 方法。
  • Servlet 调用 service() 方法来处理客户端的请求。
  • Servlet 销毁前调用 destroy() 方法。
  • 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

servlet架构 servlet基本架构代码_客户端_04

在 Servlet 的整个生命周期中,创建 Servlet 实例、init() 方法和 destory() 方法都只执行一次。当初始化完成后,Servlet 容器会将该实例保存在内存中,通过调用它的 service() 方法,为接收到的请求服务。

1. 创建Servlet实例

web容器负责加载Servlet,当web容器启动时或者是在第一次使用这个Servlet时,容器会负责创建Servlet实例,但是用户必须通过部署描述符(web.xml)指定Servlet的位置,或者在类上加上@WebServlet,成功加载后,web容器会通过反射的方式对Servlet进行实例化

@WebServlet(urlPatterns = "/Hello",loadOnStartup = 1)

loadOnStartup=1:表示在Tomcat就初始化这个Servlet容器

@WebServlet(urlPatterns = "/mayiktmeite",loadOnStartup = -1)

若为负数(-1):第一次被访问时创建Servlet对象

0或者正数:服务器启动时创建Servlet对象 数字越小优先级越高

底层会根据loadOnStartup (从0开始)值排序,越小越优先加载创建

2. WEB容器调用Servlet的init()方法,对Servlet进行初始化

在Servlet实例化之后,Servlet容器会调用init()方法,来初始化该对象,主要是为了让Servlet对象在处理客户请求前可以完成一些初始化的工作,例如,建立数据库的连接,获取配置信息等。

对于每一个Servlet实例,init()方法只能被调用一次。init()方法有一个类型为ServletConfig的参数,Servlet容器通过这个参数向Servlet传递配置信息。Servlet使用ServletConfig对象从Web应用程序的配置信息中获取以名-值对形式提供的初始化参数

另外,在Servlet中,还可以通过ServletConfig对象获取描述Servlet运行环境的ServletContext对象,使用该对象,Servlet可以和它的Servlet容器进行通信。无论有多少客户机访问Servlet,都不会重复执行init()。

3. Servlet初始化之后,将一直存在于容器中,service()响应客户端请求

  1. 如果客户端发送GET请求,容器调用Servlet的doGet方法处理并响应请求
  2. 如果客户端发送POST请求,容器调用Servlet的doPost方法处理并响应请求
  3. 或者统一用service()方法处理来响应用户请求

service()是Servlet的核心,负责响应客户的请求

每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。

要注意的是,在service()方法被容器调用之前,必须确保init()方法正确完成。容器会构造一个表示客户端请求信息的请求对象(类型为ServletRequest)和一个用于对客户端进行响应的响应对象(类型为ServletResponse)作为参数传递给service()方法。

在service()方法中,Servlet对象通过ServletRequest对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用ServletResponse对象的方法设置响应信息。

4. WEB容器决定销毁Servlet时,先调用Servlet的destroy()方法,通常在关闭web应用之前销毁Servlet

destroy()仅执行一次,在服务器端停止且卸载Servlet时执行该方法。

当容器检测到一个Servlet对象应该从服务中被移除的时候,容器会调用该对象的destroy()方法,以便让Servlet对象可以释放它所使用的资源,保存数据到持久存储设备中。例如,将内存中的数据保存到数据库中,关闭数据库的连接等。

当需要释放内存或者容器关闭时,容器就会调用Servlet对象的destroy()方法。在Servlet容器调用destroy()方法前,如果还有其他的线程正在service()方法中执行,容器会等待这些线程执行完毕等待服务器设定的超时值到达

一旦Servlet对象的destroy()方法被调用,容器不会再把其他的请求发送给该对象。如果需要该Servlet再次为客户端服务,容器将会重新产生一个Servlet对象来处理客户端的请求。在destroy()方法调用之后,容器会释放这个Servlet对象,在随后的时间内,该对象会被Java的垃圾收集器所回收

servlet 线程是否安全?

servlet 对象默认是单例,在jvm内存中只会存在一份,当多个线程如果共享到同一个全局变量可能会存在线程安全性问题