前情回顾

  • xml
  • JavaEE企业开发简介
  • web服务器Tomcat
  • eclipse+tomcat配置
  • JavaEE项目创建与发布

    今日内容

  • Servlet概述

  • Servlet接口及其实现类使用
  • Servlet配置
  • Servlet生命周期

    1、Servlet概述

    随着Web应用业务需求的增多,动态Web资源的开发变得越来越重要。目前,很多公司都提供了开发动态Web资源的相关技术,其中比较常见的有ASP、PHP、JSP和Servlet等基于Java的动态Web资源开发SUN公司提供了ServletJSP两种技术。这里将针对Servlet技术的相关知识进行详细讲解。

1.1 Servlet是什么

Servlet是运行在Web服务器端Java应用程序,它使用Java语言编写。与Java程序的区别是,Servlet 对象主要封装了对HTTP请求的处理,并且它的运行需要Servlet容器的支持。在Java Web应用方面,Servlet 的应用占有十分重要的地位,它在Web请求的处理功能方面也非常强大

1.2 Servlet容器

  1. Servlet由**Servlet容器**提供,**Servlet容器**是指**提供了Servlet 功能的服务器**(本书中指**Tomcat**)。**Servlet容器将Servlet动态地加载到服务器上**。与HTTP 协议相关的Servlet使用HTTP请求和HTTP响应与客户端进行交互。因此,Servlet容器支持所有HTTP协议的请求和响应。

1.3 Servlet执行过程

image.png
Servlet的请求首先会被HTTP服务器(如Apache)接收,HTTP服务器只负责静态HTML页面的解析,对于Servlet的请求转交Servlet容器Servlet容器根据web.xml文件中的映射关系调用相应的Servlet,Servlet将处理的结果返回给Servlet容器,并通过HTTP服务器将响应传输给客户端。

1.4 Servlet的特点

Servlet使用Java语言编写,它不仅具有Java 语言的优点,而且还对Web的相关应用进行了封装,同时Servlet容器还提供了对应用的相关扩展,无论是在功能、性能、安全等方面都十分优秀。

  • Servlet对象对Web应用进行了封装,提供了Servlet对Web应用的编程接口,还可以对HTTP请求进行相应的处理,如处理提交数据、会话跟踪、读取和设置HTTP头信息等。由于Servlet既拥有Java 提供的API,而且还可以调用Servlet封装的Servlet API编程接口,所以,它在业务功能方面十分强大
  • Java语言是跨越平台的,所谓跨越平台是指程序的运行不依赖于操作系统平台,它可以运行到多个系统平台中,如目前常用的操作系统Windows、Linux和UNIX等。
  • Servlet对象在Servlet容器启动时被初始化,当Servlet对象第一次被请求时,Servlet 容器将Servlet对象实例化,此时Servlet对象驻存于内存中。如果存在多个请求,Servlet 不会再被实例化,仍然由第一次被实例化的Servlet对象处理其他请求。每一个请求是一个线程,而不是一个进程。性能高。
  • Servlet使用了Java的安全框架,同时Servlet容器还可以为Servlet提供额外的安全功能,它的安全性是非常高的。
  • Java语言是面向对象的编程语言, Servlet由Java语言编写,所以它具有面向对象的优点。在业务逻辑处理中,可以通过封装、继承等特性扩展实际的业务需要。

1.5 Servlet接口

针对Servlet技术的开发,SUN公司提供了一系列接口和类,其中最重要的是javax.servlet.Servlet接口。Servlet就是一种实现了Servlet接口的类,它由Web容器负责创建并调用,用于接收和响应用户的请求。

1、Servlet接口的方法

image.png

2、Servlet接口中的生命周期的方法

在Servlet接口中的5个方法中,其中init()、service()destroy()3个方法可以表现Servlet的生命周期,它们会在某个特定的时刻被调用。需要注意的是,Servlet容器指的就是Web服务器。

3、Servlet接口的实现类

针对Servlet接口,SUN公司提供了两个默认的接口实现类GenericServletHttpServlet。GenericServlet是一个抽象类,该类为Servlet接口提供了部分实现,它并没有实现HTTP请求处理。HttpServlet是GenericServlet的子类,它继承了GenericServlet的所有方法,并且为HTTP请求中的POST、GET等类型提供了具体的操作方法。实际中用HttpServlet类来实现Servlet应用。

4、GenericServlet实现类

创建类,继承GenericServlet 抽象类

  1. /*
  2. Servlet快速入门
  3. */
  4. public class ServletDemo01 extends GenericServlet{
  5. @Override
  6. public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
  7. System.out.println("service方法执行了...");
  8. }
  9. }

web.xml 中配置servlet映射关系
WebContent/WEB-INF/web.xml
注意Servlet 3.0及以上版本,默认可以不用配置,使用注解( @WebServlet(“/FirsetServlet”))更方便。

在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由某一个线程从头到尾负责处理。如果一个请求需要进行IO操作,比如访问数据库、调用第三方服务接口等,那么其所对应的线程将同步地等待IO操作完成, 而IO操作是非常慢的,所以此时的线程并不能及时地释放回线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的性能问题。即便是像Spring、Struts这样的高层框架也脱离不了这样的桎梏,因为他们都是建立在Servlet之上的。为了解决这样的问题,Servlet 3.0引入了异步处理,然后在Servlet 3.1中又引入了非阻塞IO来进一步增强异步处理的性能。

  1. <!-- 省略其他内容 -->
  2. <web-app>
  3. <!-- 省略其他内容 -->
  4. <!-- 一个 Servlet,需要 servlet,servlet-mapping 两个组合配置 -->
  5. <!-- servlet 配置 开始 -->
  6. <servlet>
  7. <!-- Servlet对象信息 -->
  8. <servlet-name>ServletDemo01</servlet-name>
  9. <!-- 上面一句指定Servlet对象的名称 -->
  10. <servlet-class>com.gmxy.www.Hello</servlet-class>
  11. <!-- 上面一句指定Servlet对象的完整位置,包含包名和类名 -->
  12. </servlet>
  13. <servlet-mapping>
  14. <!-- 映射Servlet -->
  15. <servlet-name>ServletDemo01</servlet-name>
  16. <!--<servlet-name>与上面<Servlet>标签的<servlet-name>元素相对应,不可以随便起名 -->
  17. <url-pattern>/FirsetServlet</url-pattern>
  18. <!-- 上面一句话用于映射访问URL -->
  19. </servlet-mapping>
  20. <!-- servlet 配置 结束 -->
  21. </web-app>

5、Servlet 执行过程

image.png

6、HttpServlet类的常用方法及功能

image.png

7、Servlet类视图

创建Servlet,实现的方式都是选择继承GenericServlet,在Servlet的API介绍中,它提出了我们除了继承GenericServlet外还可以继承HttpServlet,通过查阅servlet的类视图,我们看到GenericServlet还有一个子类HttpServlet。同时,在service方法中还有参数ServletRequest和ServletResponse,它们的关系如下图所示:

2、Servlet开发

在实际开发中,通常都会使用 Eclipse(或IDEA等)工具完成Servlet的开发,本学期是使用Eclipse完成Servlet的开发。
实现 Servlet 有三种方式:实现 Servlet 接口、继承GenericServlet 抽象类、继承 HttpServlet 抽象类。3个类之间的关系如下:
Servlet类视图.png
通常通过继承 HttpServlet 来时开发Servlet,以下采用这种方式实现。

1、新建web项目

使用eclipse 创建动态网页项目,选择Servlet 3.0。3.0及以上支持注解及更多特性。

2、添加Tomcat的Servlet-api.jar包

找到 jar文件。
image.png

放入 WebContent\WEB-INF\lib
image.png

3、创建Servlet类

项目的java代码目录里—》新建包—》新建类 :TestServlet01
image.png

定义一个普通的class.
image.png

  1. //A: TestServlet01.java
  2. package com.gmxy.cn;
  3. public class TestServlet01 {
  4. }
  5. //B:继承 HttpServlet,用来处理http请求,实现(重写)doPost,doGet方法
  6. package com.gmxy.cn;
  7. //...
  8. public class TestServlet01 extends HttpServlet {
  9. protected void doPost(HttpServletRequest request, HttpServletResponse
  10. response) throws ServletException, IOException {
  11. }
  12. protected void doGet(HttpServletRequest request, HttpServletResponse
  13. response) throws ServletException, IOException {
  14. }
  15. }
  16. //C:为了更好的演示Servlet的运行效果,在TestServlet01的doGet()和
  17. //doPost()方法中添加一些代码。
  18. package com.gmxy.cn;
  19. //...

D、配置servlet信息
WebContent/WEB-INF/web.xml

  1. <!-- 省略其他内容 -->
  2. <web-app>
  3. <!-- 省略其他内容 -->
  4. <!-- 一个 Servlet,需要 servlet,servlet-mapping 两个组合配置 -->
  5. <!-- servlet 配置 开始 -->
  6. <servlet>
  7. <!-- Servlet对象信息 -->
  8. <servlet-name>ServletDemo01</servlet-name>
  9. <!-- 上面一句指定Servlet对象的名称 -->
  10. <servlet-class>com.gmxy.cn</servlet-class>
  11. <!-- 上面一句指定Servlet对象的完整位置,包含包名和类名 -->
  12. </servlet>
  13. <servlet-mapping>
  14. <!-- 映射Servlet/Servlet访问地址 -->
  15. <servlet-name>ServletDemo01</servlet-name>
  16. <!--<servlet-name>与上面<Servlet>标签的<servlet-name>元素相对应,不可以随便起名 -->
  17. <url-pattern>/TestServlet01</url-pattern>
  18. <!-- 上面一句话用于映射访问URL -->
  19. </servlet-mapping>
  20. <!-- servlet 配置 结束 -->
  21. </web-app>

4、启动项目,测试Servlet

  1. <br />点击1,然后运行项目。![image.png](https://cdn.nlark.com/yuque/0/2022/png/575742/1647771591121-24bf9970-9085-4a01-aa2c-1e0adf6e02a3.png#clientId=ua812aa74-fd21-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=283&id=u944d6516&margin=%5Bobject%20Object%5D&name=image.png&originHeight=283&originWidth=663&originalType=binary&ratio=1&rotation=0&showTitle=false&size=43566&status=done&style=none&taskId=u385b5481-0d36-4338-b4f7-42bf7f665a9&title=&width=663)

打开浏览器,预览效果(默认会自动打开一个浏览器)
http://localhost:8080/web/TestServlet01image.png

5、Servlet的配置详解

想让Servlet正确地运行在服务器中并处理请求信息,必须进行适当的配置,关于Servlet的配置主要有两种方式,分别是通过Web应用的配置文件web.xml、使用@WebServlet注解的方式完成。

1、使用web.xml配置Servlet

  • 通过标签进行注册,在标签下包含若干个子元素。

image.png

  • 使用标签把Servlet映射到URL地址,然后可以通过浏览器访问到这个Servlet
子标签指定要映射的Servlet名称,名称要和之前在标签下注册的相同;
子标签映射URL地址,地址前必须加“/”,否则访问不到。 实例:
Servlet: FirstServlet.java
访问url: http://localhost:8080/web/TestServlet01 xml <servlet> <!-- 声明Servlet对象 --> <servlet-name>TestServlet01</servlet-name> <!-- 上面一句指定Servlet对象的名称 --> <servlet-class>com.gmxy.www.Hello</servlet-class> <!-- 上面一句指定Servlet对象的完整位置,包含包名和类名 --> </servlet> <servlet-mapping> <!-- 映射Servlet --> <servlet-name>TestServlet01</servlet-name> <!--<servlet-name>与上面<Servlet>标签的<servlet-name>元素相对应,不可以随便起名 --> <url-pattern>/TestServlet01</url-pattern> <!-- 上面一句话用于映射访问URL --> </servlet-mapping> #### 2、@WebServlet注解属性 Servlet3.0及以上版本支持,可以用@WebServlet 注解用于代替web.xml文件中的等标签,该注解将会在项目部署时被容器处理,容器将根据具体的属性配置将相应的类部署为Servlet。为此,@WebServlet注解提供了一些属性。 image.png @WebServlet注解可以标注在任意一个继承了HttpServlet类的类之上,属于类级别的注解。下面使用@WebServlet注解标注HelloServlet类。 java @WebServlet(name = "TestServlet01",urlPatterns = "/TestServlet01") public class HelloServlet extends HttpServlet{   //处理GET方法请求的方法   public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}   //处理POST方法请求的方法   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {} } 使用@WebServlet注解将HelloServlet类标注为一个Servlet。 1. @WebServlet注解中的name属性值用于指定servlet的name属性,等价于,如果没有设置@WebServlet的name属性,其默认值是Servlet的类完整名称。 1. urlPatterns属性值用于指定一组servlet的url的匹配模式,等价于标签。 1. 如果需要在@WebServlet注解中设置多个属性,属性之间用逗号隔开。 1. 通过@WebServlet注解能极大地简化了Servlet的配置步骤,降低了开发人员的开发难度。 ### 6、Servlet 生命周期 在Servlet接口中的5个方法中,其中init()、service()destroy()3个方法可以表现Servlet的生命周期,它们会在某个特定的时刻被调用。需要注意的是,Servlet容器指的就是Web服务器。
image.png 初始化阶段
image.png 运行阶段
image.png 销毁阶段
image.png
实例:创建TestServlet02类,在TestServlet02类中编写init()方法和destroy()方法并重写service()方法,用来案例演示Servlet生命周期方法的执行效果。 java import javax.servlet.annotation.WebServlet; import javax.servlet.*; @WebServlet(name = "TestServlet02",urlPatterns="/TestServlet02") public class TestServlet02 extends GenericServlet { public void init(ServletConfig config) throws ServletException { System.out.println("init methed is called"); } public void service(ServletRequest request, ServletResponse response) throws ServletException{ System.out.println("Hello World"); } public void destroy(){ System.out.println("destroy method is called"); } } ### 7、Servlet url映射细节 ##### 1、3种url映射方式 Servlet支持三种映射方式,以达到灵活配置的目的。
创建一个Servlet java /** * 演示Servlet的映射方式 */ public class ServletDemo5 extends HttpServlet { /** * doGet方法输出一句话 */ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("ServletDemo5接收到了请求"); } //省略其他代码... }
第一种:指名道姓的方式
只有和映射配置一模一样时,Servlet才会接收和响应来自客户端的请求。
例如:映射为:/servletDemo5
访问URL:http://localhost:8585/servlet_demo/servletDemo5 ```xml ServletDemo5 com.gmxy.www.ServletDemo5 ServletDemo5 /servletDemo5 ** **<br />**第二种:/开头+通配符的方式**<br /> 此种方式,只要符合目录结构即可,不用考虑结尾是什么。<br /> 例如:映射为:/servlet/*<br /> 访问URL:[http://localhost:8585/servlet/](http://localhost:8585/servlet/itheima)abc<br /> [http://localhost:8585/servlet/](http://localhost:8585/servlet/itcast.do)def.do<br /> 这两个URL都可以。因为用的*,表示/servlet/后面的内容是什么都可以。xml ServletDemo5 com.gmxy.www.ServletDemo5 ServletDemo5 /servlet/ ``` 第三种:通配符+固定格式结尾
此种方式,只要符合固定结尾格式即可,其前面的访问URI无须关心(注意协议,主机和端口必须正确)
例如:映射为:
.do
访问URL:http://localhost:8585/servlet/gmxy.do
http://localhost:8585/gmxy.do
这两个URL都可以方法。因为都是以.do作为结尾,而前面用号通配符配置的映射,所有无须关心。 ```java ServletDemo5 com.gmxy.www.ServletDemo5 ServletDemo5 .do
<a name="ro6KN"></a> ##### 2、多路径映射Servlet 给**一个Servlet**配置**多个访问**映射(**url**),从而可以根据不同请求URL实现不同的功能。java //ServletDemo7.java protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.获取当前请求的URI String uri = req.getRequestURI(); uri = uri.substring(uri.lastIndexOf(“/“),uri.length()); //2.判断是1号请求还是2号请求 if(“/servletDemo7”.equals(uri)){ System.out.println(“ServletDemo7执行1号请求的业务逻辑:商品单价7折显示”); }else if(“/demo7”.equals(uri)){ System.out.println(“ServletDemo7执行2号请求的业务逻辑:商品单价8折显示”); }else { System.out.println(“ServletDemo7执行基本业务逻辑:商品单价原价显示”); } } 在web.xml配置Servletxml servletDemo7 com.gmxy.www.ServletDemo7

servletDemo7 /demo7

servletDemo7 /servletDemo7

servletDemo7 /servlet/*

  1. <a name="kAxra"></a>
  2. ##### 3、默认Servlet
  3. 默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下的web.xml中。
  4. 它的映射路径是**<url-pattern>/<url-pattern>**,我们在发送请求时,首先会在我们应用中的web.xml中查找映射配置,找到就执行,这块没有问题。但是当找不到对应的Servlet路径时,就去找默认的Servlet,由默认Servlet处理。
  5. <a name="hth7a"></a>
  6. ### 8、Servlet关系总图
  7. ![Servlet类关系总视图.png](https://cdn.nlark.com/yuque/0/2022/png/575742/1648185640427-4b29b0f5-e998-4dec-a75b-68b330d78989.png#clientId=uaf7e7001-e2e3-4&crop=0&crop=0&crop=1&crop=1&from=ui&height=310&id=u7c183562&margin=%5Bobject%20Object%5D&name=Servlet%E7%B1%BB%E5%85%B3%E7%B3%BB%E6%80%BB%E8%A7%86%E5%9B%BE.png&originHeight=613&originWidth=1152&originalType=binary&ratio=1&rotation=0&showTitle=false&size=503840&status=done&style=none&taskId=ua16b8f56-fb88-49c1-8152-82f622b6392&title=&width=582)
  8. <a name="v58XT"></a>
  9. ### 9、细节
  10. <a name="BjXyq"></a>
  11. #### 1、默认浏览器
  12. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/575742/1647771849601-aa3bf8ef-07e8-4eff-a0d5-846065ab1c37.png#clientId=ua812aa74-fd21-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=266&id=uc232ee26&margin=%5Bobject%20Object%5D&name=image.png&originHeight=266&originWidth=691&originalType=binary&ratio=1&rotation=0&showTitle=false&size=106129&status=done&style=none&taskId=ue6012e75-858d-4752-8405-7f29d5217bc&title=&width=691)
  13. <a name="IPAJZ"></a>
  14. #### 2、访问Servlet时出现404
  15. 前面 TestServlet01 这个类,是通过新建普通 class的方式,然后手动继承 HttpServlet类来实现的,是为了展示他的原理,默认文件编码为gbk,可能导致页面404.<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/575742/1647772491379-e5308576-4d52-4f9d-a884-6578f1d069d4.png#clientId=ua812aa74-fd21-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=227&id=u7c322a06&margin=%5Bobject%20Object%5D&name=image.png&originHeight=227&originWidth=416&originalType=binary&ratio=1&rotation=0&showTitle=false&size=16049&status=done&style=none&taskId=ud993a53f-5640-4c3c-83f9-5f88d6a66ec&title=&width=416)
  16. 1. 可以设置文件的编码为utf-8来解决。
  17. 1. 创建Servlet 的时候,选择 Servlet,而不要选择 普通类
  18. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/575742/1647772358032-44d5af66-3b77-40b3-95ed-43ebb4c07371.png#clientId=ua812aa74-fd21-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=281&id=u65ae65d2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=289&originWidth=560&originalType=binary&ratio=1&rotation=0&showTitle=false&size=87891&status=done&style=none&taskId=u76540011-3bb9-441b-a7c7-ab1e3631321&title=&width=544)
  19. <a name="eCUa4"></a>
  20. #### 3、响应中文时候乱码
  21. Servlet 输出中文到页面时候乱码。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/575742/1647773535637-7acb22e2-cadd-4ae3-b9b0-202561e90dba.png#clientId=ua812aa74-fd21-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=123&id=u6064021e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=123&originWidth=386&originalType=binary&ratio=1&rotation=0&showTitle=false&size=4564&status=done&style=none&taskId=u3e3b1c94-9927-4576-a07f-1d399197302&title=&width=386)<br />解决:
  22. ```java
  23. //1.文件编码设置为 utf-8
  24. //2.设置servlet 响应时的编码为 utf-8
  25. response.setContentType("text/html;charset=utf-8");
  26. out.print("Hello Servlet,很高兴见到你.");

image.png

3、练习

1、Servlet 基本用法
新建class,继承HttpServlet,熟悉Servlet的实现过程,细节如下:

  1. 新建2个Servelet,分别在页面打印两句话
  2. 这2个servelet,两者之间可以互相跳转

2、使用Servlet 输出如下效果
image.png