Servlet 基础

Servlet 基础

Servlet 概念

Servlet 是 Server Applet 的缩写,即运行在服务器上的小程序,实际上 Servlet 是一个接口,定义了 Java 类可以被 Tomcat 识别解析的规则,自己编写的类实现该接口,重写其中的方法,就可以成为一个 Servlet,运行在服务器上,以被浏览器客户端访问。

Servlet

使用 Servlet 步骤

现在简要介绍一下 Servlet 的实现步骤

  1. 使用 IDEA 创建一个 JavaEE 项目

创建JavaEE项目

可以看到因为有 WEB-INF 目录存在,确实为一个动态 Web 项目

ServletDemo

  1. 定义一个类,实现Servlet接口
public class ServletDemo implements Servlet {

}
  1. 实现接口中的抽象方法
    可以看到 Servlet 接口有五个抽象方法需要我们自己实现,此处先不介绍他们的具体作用,先在 service 方法中随便输出一句话
package cool.yzt.web;

import javax.servlet.*;
import java.io.IOException;

/**
 * @Author: yzt
 * @Description: ServletDemo
 */
public class ServletDemo1 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("Hello Servlet!");
    }

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

    @Override
    public void destroy() {

    }
}
  1. 使用 web.xml 文件配置 Servlet
    在 web.xml 文件中的 <web-app> </web-app> 内添加一下内容,其中 <servlet> 标签的作用是给我们自己编写的 Servlet 类(使用全类名表示)绑定一个别名, servlet-mapping 的作用是将项目的资源路径 URL 与 Servlet 类进行映射,路径可以随意
<servlet>
    <servlet-name>servletDemo1</servlet-name>
    <servlet-class>cool.yzt.web.ServletDemo1</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>servletDemo1</servlet-name>
    <url-pattern>/demo1</url-pattern>
</servlet-mapping>
  1. 启动项目,查看效果
    先确认一下 Tomcat 服务器有没有配置好,并设置一下项目的虚拟目录,依然在 Run->Edit Configurations 处设置

设置项目虚拟路径1

设置项目虚拟路径2

然后启动项目,在浏览器输入 URL http://localhost:8080/ServletDemo/demo1访问该项目的该 Servlet 资源,此时控制台输出 Hello Servlet!

Servlet 执行原理

  1. 当服务器接受到客户端浏览器的请求后,会解析请求的 URL 路径,获取该客户端要访问的 Servlet 的资源路径
  2. 查找 web.xml 文件,是否存在对应的 <url-pattern> 标签体内容
  3. 如果存在,则在找到对应的 <servlet-class> 全类名
  4. Tomcat 会将该类的字节码文件加载进内存,并且创建其对象
  5. 调用该类的方法

Servlet 生命周期

  1. 被创建

    • 默认情况下,第一次被访问时,Servlet 实例才会被创建,该实例被创建时,执行 init() 方法,且只执行一次,该方法一般用于加载资源。
    • init() 方法只执行一次,说明一个 Servlet 在内存中只存在一个实例,在多用户访问同一个 Servlet 实例时,可能出现线程安全问题,所以尽量不要在 Servlet 类中定义成员变量,即使定义了也不要在方法中对该变量进行修改
    • 可以配置 Servlet 的创建时机,在 <servlet> 标签下配置 <load-on-startup>,值为负整数表示第一次被访问时创建,值为 0 或正整数表示在服务器启动时创建
  2. 提供服务:

    • 每次访问一个 Servlet,该实例的 service() 方法就会被调用一次
  3. 被销毁:

    • 服务器关闭时,Servlet 实例就会被销毁
    • 服务器正常关闭时,Servlet 的 destroy() 方法就会执行,且只会执行一次,一般用于释放资源

Servlet3.0 注解配置

使用 web.xml 文件配置 Servlet 固然可以,但是,通常项目中可能有很多 Servlet,一个一个在文件中配置会非常麻烦,从 Servlet3.0 开始,支持注解配置,而无需 web.xml 文件。注解配置即在编写 Servlet 时,类上加一个 @WebServlet 注解,首先查看一下该注解

@Target({ElementType.TYPE}) // 该注解作用在类上
@Retention(RetentionPolicy.RUNTIME) // 保留到运行时
@Documented
public @interface WebServlet {
    String name() default "";

    String[] value() default {};

    String[] urlPatterns() default {}; // 该注解最重要的属性,指明了该注解作用的Servlet的URL

    int loadOnStartup() default -1;

    WebInitParam[] initParams() default {};

    boolean asyncSupported() default false;

    String smallIcon() default "";

    String largeIcon() default "";

    String description() default "";

    String displayName() default "";
}

所以,使用该注解时,可以通过 urlPatterns 属性指明 Servlet 的 URL

@WebServlet(urlPatterns = {"/demo2"})
public class ServletDemo2 implements Servlet {
    /*
        ... 
        其他方法
        ...
    */

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("hello annotation servlet!");
    }
}

运行项目并访问 http://localhost:8080/ServletDemo/demo2,控制台输出 hello annotation servlet!

注意

  • 一个 Servlet 的 @WebServlet 中的 urlpartten 属性可以有多个值,如 @WebServlet(urlpartten = {"/demo4","/demo4.1","/demo4.2"}),这表示这几个路径都可以访问该 Servlet
  • urlpartten 可以是单层路径(urlpartten = {"/demo4"}),也可以是多层路径(urlpartten = {"/demo4/demo1/demo"}),也可以是 *.do 即扩展名匹配,如 urlpartten = {"demo.do"},注意此时没有 /

Servlet的体系结构

通过以上实现发现,如果要实现 Servlet 接口,必须实现全部五个抽象方法,但大多数时候只需要实现 service() 方法就可以了。通过查看 JavaEE API 文档可以发现有如下继承关系

Servlet的体系结构

GenericServlet

该类将 Servlet 接口中其他的方法做了默认空实现,只将 service() 方法作为抽象,所以,定义自己的 Servlet 类时,可以直接继承该类,只实现 service() 方法即可。

@WebServlet(urlPatterns = {"/demo3"})
public class ServletDemo3 extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("hello GenericServlet!");
    }
}

HttpServlet

考虑到 Http 请求的请求方式有 7 种,其中 GET 方式和 POST 方式最常用,所以需要在 service() 方法中做出逻辑判断,如果是 GET 方式如何处理请求、如果是 POST 方式如何处理请求。HttpServlet 是对 HTTP 协议的一种封装,可以简化操作。定义自己的 Servlet 类时,可以继承 HttpServlet 类,并重写 doPost()doGet() 方法。同时,使用继承 HttpServlet 实现 Servlet 的方式在 IDEA 中是默认的,在包下新建类时可以直接选择 Creat New Servlet,会自动生成模板。

NewServlet

@WebServlet(urlPatterns = {"/demo4"})
public class ServletDemo4 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("doPost run!");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("doGet run!");
    }
}

参考

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://yzt.cool/archives/servlet基础