Servlet 的基本概念


Servlet

  • Servlet 是运行在服务端的 Java 小程序,是 Sun 公司提供一套规范,用来处理客户端请求、响应给浏览器的动态资源。
  • Servlet 的实质就是 Java 代码,通过 Java 的 API 动态的向客户端输出内容。

1. Servlet 2.5

a. Servlet 规范

  • 查阅 JavaEE 手册(帮助文档)阅读 Servlet 规范:
    在这里插入图片描述
  • 根据文档总结,了解到书写 Servlet 一个三个步骤:
    (1)创建一个 class 实现 Servlet 接口
    (2)重写 service 方法
    (3)创建的类必须在 web.xml 文件中做配置

b. 为什么要配置 web.xml?

  • 必须将请求路径和 Java 程序的对应关系建立起来。

c. Servlet 与普通的 Java 程序的区别

  1. 必须实现 Servlet 接口
  2. 必须在 Servlet 容器(服务器)中运行
  3. Servlet 程序可以接收用户请求参数以及向浏览器输出数据

d. 代码实现 Servlet 2.5 的步骤

  1. 在工程下创建 webappPractice 包,在包下创建一个类实现 Servlet 接口;
  2. 实现 service 方法;
  3. 在 web.xml 中配置书写好的 Servlet。

e. Servlet 2.5 代码实现

  • 在 webappPractice 包下创建一个类 DemoServlet 实现 Servlet 接口:
import javax.servlet.*;
import java.io.IOException;

public class DemoServlet 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() {
    }
}
  • 配置 web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>demoServlet</servlet-name>
        <servlet-class>DemoServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demoServlet</servlet-name>
        <url-pattern>/demo</url-pattern>
    </servlet-mapping>
</web-app>

f. 常见报错

  • 出现 HTTP Status 500 – Internal Server Error 可能是版本不兼容导致的文件未找到错误。
  • Console 报错:Error:java: 无效的源发行版: 12 的修复办法:
    • ctrl + alt + shift +s,以下两个选择框中选择相同版本的 JDK: 在这里插入图片描述

2. Servlet 3.0

a. Servlet 2.5 与 Servlet 3.0 的区别

  • Servlet 3.0 相较于 Servlet 2.5:
    新增了一些注解,简化 JavaWeb 代码开发,可以省略 web.xml 配置文件
    支持异步处理(多线程技术)
    支持可插性特性(书写的代码编译后生成的 class 文件可以直接部署到其他项目的,自动加载执行)

b. 代码实现 Servlet 3.0 的步骤

  1. 创建 JavaEE 6(含 6)以上的工程;
  2. 创建 Servlet,在 @WebServlet 注解中添加 urlPatterns = “/hello”,作为请求路径。

c. Servlet 3.0 代码实现

  1. 创建 JavaEE 6 以上(含 6)的工程:
    在这里插入图片描述
  • 3.0 以上的版本没有了 web.xml:
    在这里插入图片描述
  1. 注解开发 Servlet 代码演示:
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

//name = "HelloServlet":servlet名称,相当于web.xml中的<servlet-name>
//urlPatterns = "/hello":servlet的访问路径,相当于<url-pattern>
@WebServlet(name = "DemoServlet", urlPatterns = "/demo")
public class DemoServlet 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 3.0");
    }

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

    @Override
    public void destroy() {
    }
}

3. Servlet 生命周期

a. 什么是生命周期?

  • 一个对象从创建到消亡的过程,就是生命周期。因此,Servlet 的生命周期就是指 Servlet 什么时候创建,什么时候销毁的过程。

b. Servlet 生命周期相关的方法

  • 首先来回顾 Servlet 接口的文档内容,其中一部分如图所示:
    在这里插入图片描述

i. API 介绍

  • 上图中,可以注意到两个点,Servlet 的创建和销毁由两个相关的方法 init 方法和 destroy 方法。
void destroy() 销毁servlet的方法 
void init(ServletConfig config) 初始化servlet的方法
  • 调用测试一下这两个方法:

ii. 使用步骤

  1. 创建 LifeCycleServlet 初始化
  2. 复写 init、service、destroy 方法
  3. 访问 Servlet 测试初始化 LifeCycleServlet
  4. 关闭服务器测试销毁 LifeCycleServlet

iii. Servlet 2.5 代码实现

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import java.io.IOException;

public class LifeCycleServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        super.init();
        System.out.println("LifeCycleServlet 初始化。。。");
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("LifeCycleServlet 执行。。。");
    }

    @Override
    public void destroy() {
        super.destroy();
        System.out.println("LifeCycleServlet 销毁。。。");
    }
}
  • 配置 web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>LifeCycleServlet</servlet-name>
        <servlet-class>LifeCycleServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LifeCycleServlet</servlet-name>
        <url-pattern>/life</url-pattern>
    </servlet-mapping>
</web-app>
  • 访问servlet(http://localhost:8080/webappPractice/life):
    LifeCycleServlet初始化。。。
    LifeCycleServlet执行。。。
  • 关闭tomcat服务器:
    LifeCycleServlet销毁。。。
  • 查看 Console:
    在这里插入图片描述

c. Servlet 生命周期流程

  • 虽然简单使用过了 Servlet 生命周期相关的方法,但是 Servlet 从创建到销毁的过程还是没说清楚,因此,接下来会以时序图的方式展示 Servlet 的运行过程,注意图中每一步都由序号,按照序号查看每一个步骤。
    在这里插入图片描述
  • Servlet 在初始化一次之后,就不再创建,因此如果多次访问同一个 Servlet 的效果是这样的:
    LifeCycleServlet初始化。。。
    LifeCycleServlet执行。。。
    LifeCycleServlet执行。。。
    LifeCycleServlet执行。。。
  • 因此 Servlet 是一个单例线程不安全的对象。

4. Servlet 的体系结构

  • 目前已经介绍了创建一个类实现 Sevlet 接口的方式开发 Servlet 程序,实现 Servlet 接口的时候,必须实现接口的所有方法。但是,在 Servlet 中,真正执行程序逻辑的是 service,对于 Servlet 的初始化和销毁,由服务器调用执行,开发者本身不需要关心。因此,有没有一种更加简洁的方式来开发 Servlet 程序呢?
  • 查阅 API 回顾 Servlet 接口:
    在这里插入图片描述
  • 由上图可知在 Servlet 接口规范下,官方推荐使用继承的方式,继承 GenericServlet 或者 HttpServlet 来实现接口,接下来再去查看一下这两个类的 API:

a. GenericServlet

在这里插入图片描述

  • 阅读上图 API 可知,GenericServlet 是一个类,它简化了 Servlet 的开发,已经提供好了一些 Servlet 接口所需的方法,开发者只需要重写 service 方法即可。
  • 使用 GenericServlet 创建 Servlet 的步骤:
    • 创建一个类
    • 继承 GenericServlet
    • 重写 service 方法
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class DemoGenericServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("DemoGenericServlet执行.......");
    }
}
  • web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>demoGenericServlet</servlet-name>
        <servlet-class>DemoGenericServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demoGenericServlet</servlet-name>
        <url-pattern>/generic</url-pattern>
    </servlet-mapping>
</web-app>

b. HttpServlet

  • 虽然,GenericServlet 已经简化了 Servlet 开发,但是平时开发程序需要按照一种互联网传输数据的协议来开发程序——HTTP 协议,因此, Sun 公司又专门提供了 HttpServlet,来适配这种协议下的开发。
    在这里插入图片描述
  • 阅读上图的 API 可知,继承 HttpServlet,需要重写 doGet、doPost 等方法中一个即可,根据 HTTP 不同的请求,实现相应的方法。
  • 使用 HttpServlet 创建 Servlet 的步骤:
    • 创建一个类
    • 继承 HttpServlet
    • 重写 doGet 方法
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

public class DemoGenericServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("DemoGenericServlet执行.......");
    }
}
  • web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>demoHttpServlet</servlet-name>
        <servlet-class>DemoHttpServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demoHttpServlet</servlet-name>
        <url-pattern>/http</url-pattern>
    </servlet-mapping>
</web-app>
  • 通过以上两个 API 阅读,注意到 HttpServlet 是 GenericServlet 的子类,它增强了 GenericServlet 一些功能,因此,在后期使用的时候,一般选择继承 HttpServlet 来开发 Servlet 程序。
  • 既然目前开发 Servlet 的选择继承类已经确定,那么这些浏览器、服务器、Servlet 等,到底是如何运行的?接下来将介绍 Servlet 的运行原理。

c. Servlet 的运行原理

在这里插入图片描述

  1. Servlet 对象是由 Tomcat 服务器创建;
  2. request 与 response 对象也是由 Tomcat 服务器创建;
  3. request 对象封装了浏览器过来的所有请求信息,response 对象代表了服务器的响应信息。

5. Servlet 的配置

a. url-pattern 配置

  • 在创建 Servlet 后,如果想要这个 Servlet 可以被访问到,必须在 web.xml 文件中对其进行配置。在其中有一个标签 url-pattern 是用于确定访问一个 Servlet 的路径,它是用于确定访问的 Servlet 路径,接下来详细介绍一下关于这个标签的配置。
  • 一个 Servlet 是否可以被不同的路径映射?(即多个不同配置是否可以映射同一个 Servlet) 答案是肯定的。可以通过下面的示例来说明上面的问题:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>demoHttpServlet</servlet-name>
        <servlet-class>DemoHttpServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demoHttpServlet</servlet-name>
        <url-pattern>/http</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>demoHttpServlet</servlet-name>
        <url-pattern>/http2</url-pattern>
    </servlet-mapping>
</web-app>
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>
  • 优先级: 完全匹配 > 目录匹配 > 扩展名匹配
  • 注意:当它的值是 / 时,它表示一个默认(缺省)的 Servlet。默认的 Servlet 其作用是用于处理其它的 Servlet 处理不了的请求。例如:
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

b. load-on-startup 配置

  • 在标签中添加了一个 load-on-startup,设定值为2,那么这时 DemoHttpServlet 就会在服务器启动时,跟随启动。 注意:值越小代表的是优先级越高。例如:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>demoHttpServlet</servlet-name>
        <servlet-class>DemoHttpServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>demoHttpServlet</servlet-name>
        <url-pattern>/http</url-pattern>
    </servlet-mapping>
</web-app>

6. Servlet 的路径

  • 在开发中,经常在页面上通过表单或超连接向服务器发送请求,如果我们访问的是一个 Servlet,那么这时访问 Servlet 的路径应该如何书写?

a. 访问服务器端资源的方式

  1. 在地址栏上直接输入 URL;
  2. 超连接的方式;
  3. 通过表单方式;
  4. 通过 JS 的 location.href 方式。
  • 对于以上方式,只有表单提交的方式才可能有 POST 请求,其它的都是 GET 请求。

b. doGet 和 doPost 方法的区别

  1. doGet() 和 doPost() 分别对应 HTTP 协议中的 GET 和 POST 方法,请求是 GET 方法就调用 doGet() ,请求是 POST 方法就调用 doPost() 方法。
  2. GET 和 post 方法有本质的区别:
    GET 只有一个流,参数附加到 URL 后,大小个数都有严格的限制,且只能是字符串;
    POST 的参数不通过 URL 而是通过其他流的传递参数,所以可以很大,也可以传递二进制数据,例如上传文件。
  3. POST 比 GET 安全:
    由 Web 浏览器或通过 HTML、JSP 直接访问 Servlet 的 URL 时,一般用 GET 调用。GET 调用会把传递给 Servlet 的参数在 URL 里显示出来,这样对系统安全方面存在问题;
    而 POST 请求则通过其他流传递参数,不会在 URL 中显示,更安全;例如用户登录时,如果是用 GET 调用会把用户账号密码都显示在 URL 中,用 POST 调用则不会显示。
  4. 服务器接收方式:
    服务器随机接受 GET 方法的数据,一旦断电等原因,服务器也不知道信息是否发送完毕;
    而 POST 方法,服务器先接受数据信息的长度,然后再接受数据。
  • 这种区别是浏览器完成的功能,不需要在 doGet 和 doPost 方法中体现。
  • get 和 post 都是明文,都不安全,用 HTTPS 才安全。

c. Servlet 的访问路径

  • 创建 DemoServelet 并继承 HttpServlet:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class DemoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("get请求访问");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("post请求访问");
    }
}
  • 配置 web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
		  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>demoServlet</servlet-name>
        <servlet-class>DemoServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>demoServlet</servlet-name>
        <url-pattern>/demo</url-pattern>
    </servlet-mapping>
</web-app>
  • 修改 index.jsp,访问页面:
<%--
  Created by IntelliJ IDEA.
  User: 80626
  Date: 2020/4/13
  Time: 11:17
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  $END$

  <h1>带协议的绝对路径</h1>
  <a href="http://localhost:8080/webappPractice/demo">DemoServlet</a>
  <h1>不带协议的绝对路径</h1>
  <a href="/webappPractice/demo">DemoServlet</a>
  <h1>相对路径</h1>
  <a href="demo">DemoServlet</a>
  </body>
</html>
  • 以上访问均是 get 请求,均可以在 Console 输出 get请求访问
  • 开发中使用不带协议的绝对路径会比较多一些;带协议的绝对路径只有在访问站外资源时才会使用;相对路径需要分析文件间的相对关系才能使用,例如:如果上面的 index.jsp 移动到 http://localhost:8080/webappPractice/main 目录下,那么这三种方式中,只有相对路径会报错,要是想正常访问,可以修改为:<a href="../demo">DemoServlet</a>

原文链接:https://qwert.blog.csdn.net/article/details/105481464

发布了369 篇原创文章 · 获赞 381 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Regino/article/details/105481464