Tomcat&Servlet基础&Request对象
Servlet、Tomcat基础知识点,仅供参考
19年黑马JavaWeb课程的笔记
Servlet? Tomcat?
在讲解Servlet之前,先补充一些web服务器的知识,所谓服务器,通俗上的理解就是安装了服务器软件的计算机。这些服务器软件通过接受用户请求、处理用户请求,回传响应来实现提供某一种服务。
而Tomcat就可以理解为是一种服务器软件。
Servlet全名是:server applet,也就是运行在服务端的小程序,这些小程序配合服务器软件处理具体的业务。因此通常服务器接收到具体的请求的时候,服务器会将请求分配给相应的servlet进行处理,再将servlet的响应结果回传给用户。

Tomcat服务器安装
下载地址:Apache Tomcat® - Welcome!
解压压缩包,建议不带中文,可以看到Tomcat服务器的如下目录结构:


在bin目录下双击startup.bat即可启动tomcat

Tomcat部署配置

Servlet配置
环境配置——IDEA
首先新建web项目,这个在不同版本的idea中都不同,通常都是选择:
1 | new --> project -->java Enterprise -->Web project | Servlet |
然后就是配置Tomcat了,顺带一提,下载tomcat之后,不要乱改bin里面的catalina.bat,我当时就是因为乱码问题作死改了这个配置里面的 JAVA_OPTS(set JAVA_OPTS = -Dfile = utf-8),然后idea就一直报错了,错误如下:
1 | unable to ping server localhost:1099 |
idea会帮你配置JAVA_OPTS, 如果你配置了,那么你的配置将会覆盖掉idea的(坑爹啊,艹)
配置可以参考下面的图:


环境配置——Eclipse
基于XML的配置
创建
JavaEE项目,项目名称(虚拟目录)为:/servletTest,就是上图的那个Application context定义一个类,实现Servlet接口
实现接口中的抽象方法
1 | public class ServletDemo1 implements Servlet{ |
- 配置Servlet, 在web.xml中配置:
1 | <!--配置Servlet --> |
- Link Start
1 | http://localhost:8080/servletTest/demo1 |
执行原理
- 当服务器接受到客户端浏览器的请求后,会解析请求
URL路径,获取访问的Servlet的资源路径 - 查找
web.xml文件,是否有对应的<url-pattern>标签体内容。 - 如果有,则在找到对应的
<servlet-class>全类名 tomcat会将字节码文件加载进内存,并且创建其对象- 调用其方法
基于注解的配置
- 创建
JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml - 定义一个类,实现
Servlet接口 - 复写方法
1
2
3
4
5
6
7
8public class ServletDemo1 implements Servlet{
...
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello World!");
}
...
} - 在类上使用
@WebServlet注解,进行配置
1 |
这个注解的源代码如下:
1 |
|
1 |
|
Servlet 生命周期
一个servlet程序要想在Web服务器上运行,一般需要四个步骤:
- 装入
- 初始化
- 提供服务
- 销毁
其中程序员能决定,从步骤2开始,到步骤4,装入一般交给web服务器,准确点来说是服务器软件,如Tomcat负责。
初始化
初始化,也就是实例化Servlet的具体实现类的工作,这个动作会调用Servlet接口的init(),在整个servlet生命周期中只会执行一次。
那这里就产生了两个问题
Servlet什么时候初始化?
默认情况下,第一次被访问时,Servlet类就被初始化,可以配置执行Servlet的创建时机。
在<servlet>标签下配置
- 第一次被访问时创建,
<load-on-startup>的值为负数 - 在服务器启动时创建,
<load-on-startup>的值为0或正整数
Servlet是单例的
Servlet的初始化在整个生命周期过程中只执行一次,这就说明对于每一个Servlet的具体实现类被加载之后,必须要走完整个生命周期,才能再次创建相同实现类的实例,因此在内存中只存在唯一对象,Servlet是单例的,一旦涉及到单例,就可能存在线程安全问题。
因此尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要修改值
提供服务
该过程会执行service(),执行多次,每次访问Servlet时,Service方法都会被调用一次。
由于对Servlet接口的进一步封装,现在也很少直接在service()进行编码,一般使用的是HttpServlet的doGet()、doPost()等
销毁
执行destroy方法,只执行一次,Servlet被销毁时执行。服务器关闭时,Servlet被销毁
- 只有服务器正常关闭时,才会执行destroy方法。
- destroy方法在
Servlet被销毁之前执行,一般用于释放资源
Servlet API及体系结构
一般自定义的servlet都是派生于:HttpServlet

其中GenericServlet和HttpServlet都是抽象类,GenericServlet只提供一个抽象方法service(),对Servlet中的其他方法都做了默认实现。因此如果需要继承GenericServlet,只需要实现一个方法:service()
而对于HttpServlet来说,Http有7中请求方式,将这7种方式都封装在HttpServlet,它们都是:doXXX()这样的方法!
实际上观察HttpServlet的service()方法可以看到:

首先getMethod()返回请求方式,如果是post,调用doPost(),如果是get,调用doGet(),以此类推。
实际上对于HttpServlet,常用的两个方法是:doGet()和doPost(),因此继承自HttpServlet的Servlet类只需要重写这两个方法即可。
分类
Servlet实现相关
- javax.servlet.Servlet
- javax.servlet.GenericServlet
- javax.servlet.http.HttpServlet
Servlet配置文件相关(Web.xml解析结果)
- javax.servlet.ServletConfig
响应和请求
- javax.servlet.ServletResponse
- javax.servlet.ServletRequest
- javax.servlet.http.HttpServletResponse
- javax.servlet.http.HttpServletRequest
会话跟踪
- javax.servlet.http.HttpSession
- javax.servlet.http.HttpSessionBindingListener
- javax.servlet.http.HttpSessionBindingEvent
Servlet上下文环境
- javax.servlet.ServletContext
Servlet协作
- javax.servlet.RequestDispatcher
Cookie
- javax.servlet.http.Cookie
Utils
- javax.servlet.http.HttpUtils
再谈URLPartten
在前面的Servlet配置中,我们详细介绍对于servlet中的URL如何配置,本小节我们继续深入探讨配置URL的几种方式。

首先在WebServlet里面,可以看到:
1 | String[] urlPatterns() default {}; |
urlPatterns实际上却可以接受一个数组,这说明对于一个servlet它可以有多个访问的URL,如:
1 | // Application Context: servletDemo |
这样当你本地的tomcat服务器启动后,你在浏览器输入下面三个地址都会链接到该servlet:
1 | localhost:8080/servletDemo/demo1 |
当然对于一个servlet,你也可以配置多层访问URL,如:
1 | // Application Context: servletDemo |
这样当你本地的tomcat服务器启动后,你需要这样才能够访问这个servlet:
1 | localhost:8080/servletDemo/user/demo1 |
如果你只写这样:
1 | localhost:8080/servletDemo/user |
相信我,绝对会404
同时URL也是支持通配符*的,如:
1 | // Application Context: servletDemo |
后面的*无论你输入什么都可以跳转到该servlet,如:
1 | localhost:8080/servletDemo/user/askia |
这些URL都会跳转到这个Servlet里面,当然:如果你只是输入:
1 | localhost:8080/servletDemo/Askia |
那也会404,要看准链接!
Request
HTTP请求消息数据格式
关于HTTP的具体内容这里不具体阐述,可以看这篇文章,这里只放出关于HTTP的请求消息数据格式的解析。
我们浏览器一旦有处理一次HTTP请求,都会发一次类似这样的消息:
1 | POST /login.html HTTP/1.1 |
请求行
请求行数据就是HTTP请求数据的第一行,也就是:
1 | POST /login.html HTTP/1.1 |
这里面包含了请求方式,请求URL,请求协议/版本,如上面的请求消息解析出来就是:
1 | 请求方式:Post |
请求头
请求头通常是键值对存在,包含非常多的信息:
1 | Host: localhost |
其中比较重要的是:
- User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息,可以在服务器端获取该头的信息,解决浏览器的兼容性问题
- Referer: 告诉服务器,我(当前请求)从哪里来?,如:
http://localhost/login.html,这个东西的作用有两个:防盗链和统计工作。
请求空行
就是Upgrade-Insecure-Requests: 1和username=zhangsan之间的那个空行,用于分割POST请求的请求头和请求体。
请求体(正文)
封装POST请求消息的请求参数的,只有POST请求方式,才有请求体,如:
1 | username=zhangsan |
Request对象继承体系结构

可以看到上面的继承体系,和Servlet的继承体系如出一辙,其中HttpServletRequest只能在HttpServlet中见到。在GenericServlet中常见到的是ServletRequest
Request功能
获取请求行信息的方法
对于请求行数据:
1 | GET /day14/demo1?name=zhangsan&user=lisi |
1 | String getMethod() // 获取请求方式 :GET |
获取请求头信息的方法
之前说过请求头信息是一组键值对,因此:
1 | String getHeader(String name); // 通过请求头的名称获取请求头的值 |
1 | request.getHeader("User-Agent"); |
获取请求体信息的方法
1 | BufferedReader getReader() // 获取字符输入流,只能操作字符数据(文本) |
对于这个请求体:
1 | username=zhangsan |
获取它的方法:
1 | String str = request.getReader().readLine(); // str = username=zhangsan |
Request额外功能
更加通用的获取请求参数方法
前面在请求头里面介绍过获取请求参数的方法:String getQueryString() 这个方法获取到的参数是:XXXXX=XXXX&XXXX=XXX格式的,并且只能是Get方式的请求参数,有没有更加通用的方法,既能获取Get请求参数也能获取Post请求参数,有:
1 | String getParameter(String name); // 根据参数名称获取参数值 |
上面四个方法,对于Post、Get都通用,对于下面的请求参数:
1 | /day14/demo1?name=zhangsan&user=lisi |
只需要使用:
1 | String s = request.getParameter("name"); // s = zhangsan |
同时有时候需要使用到多个参数的时候,比如说:
1 | hobby=xx&hobby=game |
两个Hobby,那就需要使用:
1 | String[] ss = request.getParameterValues("hobby"); // ss = {"xx", "game"} |
防止乱码现象
乱码现象在Get方式中一般不会出现,tomcat 8 已经将Get方式乱码问题解决了,当然如果你使用tomcat 7,或者你使用的是Post方式你可以在获取请求参数之前使用:
1 | request.setCharacterEncoding("utf-8"); |
来防止乱码现象
请求转发
Request可以实现服务器内部的资源跳转,例如,你现在有两个Servlet,URL什么的已经配置好了,如:
1 |
|
我希望在Servlet1处理完之后能够跳转到Servlet2,做一个请求的转发,要做到这点需要两个步骤:
- 通过request对象获取请求转发器对象:
1 | RequestDispatcher getRequestDispatcher(String path) |
- 使用
RequestDispatcher的forward()进行转发:
1 | void forward(ServletRequest request, ServletResponse response) |
如上面的例子,我们可以这样写转发:
1 |
|
请求转发有三个特点:
- 浏览器地址栏路径不发生变化
调用了request.getRequestDispatcher("/demo2").forward(request, response);之后浏览器的URL还是/demo1,不会因为调用请求转发而改变。 - 只能转发到当前服务器内部资源中。
request.getRequestDispatcher("www.baidu.com")是没法完成请求转发的! - 转发是一次请求
实际上细心的同学还会发现,请求转发还有一种方式,那就是RequestDispatcher的include():
1 | void include(ServletRequest request, ServletResponse response) |
include方式的转发和forward方式的转发实现的功能是一样的,区别在于请求URL的变化。
还有include方式的转发,不能设置response对象的HTTP头和状态码,而forward可以。
include方式的转发,转发之后可以使用out.println(),forward不可以
数据共享
域对象:一个有作用范围的对象,可以在范围内共享数据。
request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据
1 | void setAttribute(String name,Object obj) // 存储数据 |
结合请求转发来使用,可以实现多个Servlet之间的数据共享!
获取ServletContext(Web.xml上的内容)
1 | ServletContext getServletContext() |