前情回顾
- xml
- JavaEE企业开发简介
- web服务器Tomcat
- eclipse+tomcat配置
-
今日内容
Servlet概述
- Servlet接口及其实现类使用
- Servlet配置
- Servlet生命周期
1、Servlet概述
随着Web应用业务需求的增多,动态Web资源的开发变得越来越重要。目前,很多公司都提供了开发动态Web资源的相关技术,其中比较常见的有ASP、PHP、JSP和Servlet等。基于Java的动态Web资源开发,SUN公司提供了Servlet和JSP两种技术。这里将针对Servlet技术的相关知识进行详细讲解。
1.1 Servlet是什么
Servlet是运行在Web服务器端的Java应用程序,它使用Java语言编写。与Java程序的区别是,Servlet 对象主要封装了对HTTP请求的处理,并且它的运行需要Servlet容器的支持。在Java Web应用方面,Servlet 的应用占有十分重要的地位,它在Web请求的处理功能方面也非常强大。
1.2 Servlet容器
Servlet由**Servlet容器**提供,**Servlet容器**是指**提供了Servlet 功能的服务器**(本书中指**Tomcat**)。**Servlet容器将Servlet动态地加载到服务器上**。与HTTP 协议相关的Servlet使用HTTP请求和HTTP响应与客户端进行交互。因此,Servlet容器支持所有HTTP协议的请求和响应。
1.3 Servlet执行过程
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接口的方法
2、Servlet接口中的生命周期的方法
在Servlet接口中的5个方法中,其中init()、service()和destroy()这3个方法可以表现Servlet的生命周期,它们会在某个特定的时刻被调用。需要注意的是,Servlet容器指的就是Web服务器。
3、Servlet接口的实现类
针对Servlet接口,SUN公司提供了两个默认的接口实现类:GenericServlet和HttpServlet。GenericServlet是一个抽象类,该类为Servlet接口提供了部分实现,它并没有实现HTTP请求处理。HttpServlet是GenericServlet的子类,它继承了GenericServlet的所有方法,并且为HTTP请求中的POST、GET等类型提供了具体的操作方法。实际中用HttpServlet类来实现Servlet应用。
4、GenericServlet实现类
创建类,继承GenericServlet 抽象类
/*
Servlet快速入门
*/
public class ServletDemo01 extends GenericServlet{
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service方法执行了...");
}
}
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来进一步增强异步处理的性能。
<!-- 省略其他内容 -->
<web-app>
<!-- 省略其他内容 -->
<!-- 一个 Servlet,需要 servlet,servlet-mapping 两个组合配置 -->
<!-- servlet 配置 开始 -->
<servlet>
<!-- Servlet对象信息 -->
<servlet-name>ServletDemo01</servlet-name>
<!-- 上面一句指定Servlet对象的名称 -->
<servlet-class>com.gmxy.www.Hello</servlet-class>
<!-- 上面一句指定Servlet对象的完整位置,包含包名和类名 -->
</servlet>
<servlet-mapping>
<!-- 映射Servlet -->
<servlet-name>ServletDemo01</servlet-name>
<!--<servlet-name>与上面<Servlet>标签的<servlet-name>元素相对应,不可以随便起名 -->
<url-pattern>/FirsetServlet</url-pattern>
<!-- 上面一句话用于映射访问URL -->
</servlet-mapping>
<!-- servlet 配置 结束 -->
</web-app>
5、Servlet 执行过程
6、HttpServlet类的常用方法及功能
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个类之间的关系如下:
通常通过继承 HttpServlet 来时开发Servlet,以下采用这种方式实现。
1、新建web项目
使用eclipse 创建动态网页项目,选择Servlet 3.0。3.0及以上支持注解及更多特性。
2、添加Tomcat的Servlet-api.jar包
找到 jar文件。
3、创建Servlet类
项目的java代码目录里—》新建包—》新建类 :TestServlet01
定义一个普通的class.
//A: TestServlet01.java
package com.gmxy.cn;
public class TestServlet01 {
}
//B:继承 HttpServlet,用来处理http请求,实现(重写)doPost,doGet方法
package com.gmxy.cn;
//...
public class TestServlet01 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
}
}
//C:为了更好的演示Servlet的运行效果,在TestServlet01的doGet()和
//doPost()方法中添加一些代码。
package com.gmxy.cn;
//...
D、配置servlet信息
WebContent/WEB-INF/web.xml
<!-- 省略其他内容 -->
<web-app>
<!-- 省略其他内容 -->
<!-- 一个 Servlet,需要 servlet,servlet-mapping 两个组合配置 -->
<!-- servlet 配置 开始 -->
<servlet>
<!-- Servlet对象信息 -->
<servlet-name>ServletDemo01</servlet-name>
<!-- 上面一句指定Servlet对象的名称 -->
<servlet-class>com.gmxy.cn</servlet-class>
<!-- 上面一句指定Servlet对象的完整位置,包含包名和类名 -->
</servlet>
<servlet-mapping>
<!-- 映射Servlet/Servlet访问地址 -->
<servlet-name>ServletDemo01</servlet-name>
<!--<servlet-name>与上面<Servlet>标签的<servlet-name>元素相对应,不可以随便起名 -->
<url-pattern>/TestServlet01</url-pattern>
<!-- 上面一句话用于映射访问URL -->
</servlet-mapping>
<!-- servlet 配置 结束 -->
</web-app>
4、启动项目,测试Servlet
<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/TestServlet01
5、Servlet的配置详解
想让Servlet正确地运行在服务器中并处理请求信息,必须进行适当的配置,关于Servlet的配置主要有两种方式,分别是通过Web应用的配置文件web.xml、使用@WebServlet注解的方式完成。
1、使用web.xml配置Servlet
- 通过
标签 进行注册,在标签下包含若干个子元素。
- 使用
标签 把Servlet映射到URL地址,然后可以通过浏览器访问到这个Servlet
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文件中的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属性,等价于初始化阶段
运行阶段
销毁阶段
实例:创建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
** **<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
此种方式,只要符合固定结尾格式即可,其前面的访问URI无须关心(注意协议,主机和端口必须正确)
例如:映射为:.do
访问URL:http://localhost:8585/servlet/gmxy.do
http://localhost:8585/gmxy.do
这两个URL都可以方法。因为都是以.do作为结尾,而前面用号通配符配置的映射,所有无须关心。 ```java
<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配置Servlet
xml
<a name="kAxra"></a>
##### 3、默认Servlet
默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下的web.xml中。
它的映射路径是**<url-pattern>/<url-pattern>**,我们在发送请求时,首先会在我们应用中的web.xml中查找映射配置,找到就执行,这块没有问题。但是当找不到对应的Servlet路径时,就去找默认的Servlet,由默认Servlet处理。
<a name="hth7a"></a>
### 8、Servlet关系总图
![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)
<a name="v58XT"></a>
### 9、细节
<a name="BjXyq"></a>
#### 1、默认浏览器
![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)
<a name="IPAJZ"></a>
#### 2、访问Servlet时出现404
前面 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)
1. 可以设置文件的编码为utf-8来解决。
1. 创建Servlet 的时候,选择 Servlet,而不要选择 普通类
![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)
<a name="eCUa4"></a>
#### 3、响应中文时候乱码
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 />解决:
```java
//1.文件编码设置为 utf-8
//2.设置servlet 响应时的编码为 utf-8
response.setContentType("text/html;charset=utf-8");
out.print("Hello Servlet,很高兴见到你.");
3、练习
1、Servlet 基本用法
新建class,继承HttpServlet,熟悉Servlet的实现过程,细节如下:
- 新建2个Servelet,分别在页面打印两句话
- 这2个servelet,两者之间可以互相跳转
2、使用Servlet 输出如下效果