Servlet开发¶
约 7644 个字 641 行代码 6 张图片 预计阅读时间 33 分钟
动态资源和静态资源介绍¶
静态资源:无需在程序运行时通过代码运行生成的资源,在程序运行之前就写好的资源。例如:HTML、CSS、JS、图片、音频文件和视频文件
动态资源:需要在程序运行时通过代码运行生成的资源,在程序运行之前无法确定的数据,运行时动态生成。例如Servlet、Thymeleaf……
Note
动态资源指的不是视图上的动画效果或者是简单的人机交互效果
Servlet简介和工作流程介绍¶
Servlet工作流程可以大致如下图所示:
基本过程如下:
- 客户端向服务器端发出请求,服务器端的软件Tomcat接收到用户请求后会将请求报文的信息转换为
HttpServletRequest
对象,该对象中包含着请求中的所有信息,例如请求头、请求行。需要注意,这一过程中的HttpServletRequest
对象并不是由程序员手动创建的,而是由Tomcat自动创建,并且此时除了存在HttpServletRequest
对象以外,还有一个HttpServletResponse
对象,该对象用于存储响应报文信息 - 在整个过程中,Tomcat根据请求中的URL路径找到指定的Servlet类,此时将类
UserServlet
实例化,调用其service
方法,同时传递实参给HttpServletRequest req
和HttpServletResponse resp
,此时在service
方法中需要处理请求的信息,再将处理结果存储到响应对象resp
中返回给客户端即可
Servlet初使用¶
有了前面对Servlet工作流程的简单了解,接下来就可以根据上面的流程分析出以下的步骤:
- 创建一个类并且实现
Servlet
接口 - 重写其中的
service
方法 - 获取客户端的请求信息
- 处理客户端的请求信息(处理业务)
- 将处理结果放入响应对象中
Note
需要注意,整个过程中的两个对象HttpServletRequest
和HttpServletResponse
都是引入传递,所以service
方法不需要返回值
根据上面的代码就可以写出下面的UserServlet
类:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
但是实际上只需要使用到service
方法,而不需要重写其他方法,但是因为Servlet
本身是一个接口,其中的所有方法都是抽象方法,所以直接实现该接口的子类UserServlet
就必须重写所有方法:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
为了避免这种问题,可以考虑使用继承,但并不是继承Servlet
接口,而是继承其子类HttpServlet
,其子类HttpServlet
继承自GenericServlet
,GenericServlet
继承自Servlet
,而因为HttpServlet
实现了Servlet
中的所有接口,所以此时UserServlet
继承该子类就可以按需重写对应的方法,此时上面的UserServlet
就可以修改为如下代码:
Java | |
---|---|
1 2 3 4 5 6 |
|
现在有一个需求:判断用户输入的用户名是否是admin
,如果是admin
,就提示Wrong Username
,否则提示Correct Username
根据需求,首先需要一个页面获取用户的输入信息:
HTML | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
在上面的页面中,创建了一个form
表单,但是其action
属性暂时留空,因为需要先处理好请求映射路径,再将该路径作为action
的值,method
可以为get
也可以为post
接着编写Java代码,实现一个UserServlet
类,该类继承自HttpServlet
并重写service
方法,根据前面的分析,在这个方法中需要进行三步:
- 根据
name
的值username
获取到用户输入框中的内容 - 判断输入框内容是否是
admin
,如果是,则向客户端响应Wrong Username
,否则响应Correct Usernamme
- 将结果写入响应对象中
根据上面的三步,细化到代码中的步骤如下:
- 调用请求对象
req
的方法getParameter("username")
获取到输入框中的值。之所以可以这样获取,本质是因为不论是get
请求还是post
请求,输入框传递的参数都是键值对的形式,获取时只要有了键,就可以根据这个键获取其对应的值 - 判断
getParameter
方法的返回值是否与admin
相等,如果相等,说明用户输入的内容是admin
,此时结果为Wrong Username
,否则结果为Correct Username
,因为需要将结果存储到响应对象,所以此处还需要一个变量存储最终结果 - 调用响应对象的
getWriter()
方法创建一个向响应体中打印字符的响应流,将结果变量的值写入到响应流中
示例代码如下:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
有了页面和处理请求的UserServlet
,接下来就是让页面向该UserServlet
发送请求,此时就需要编写Servlet
请求映射路径,这个请求路径在WEB-INF
文件夹中的web.xml
文件中编写,编写步骤如下:
- 创建
<servlet></servlet>
标签,其中有两个子标签:<servlet-name></servlet-name>
:表示目标Servlet类的别名<servlet-class></servlet-class>
:表示目标Servlet类的全路径名
- 在
<servlet></servlet>
标签下创建一个兄弟标签<servlet-mapping></servlet-mapping>
,其中有两个子标签:<servlet-name></servlet-name>
:表示需要指向的目标Servlet类的别名,与<servlet></servlet>
中的<servlet-name></servlet-name>
内容相同<url-pattern></url-pattern>
:表示目标Servlet类的映射路径
例如下面的代码:
XML | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
有了请求映射地址,现在就可以完善HTML中的form
标签中的action
属性值,注意action
属性值不要带请求映射地址的/
:
HTML | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
编写完上面的内容后,配置好Tomcat运行环境就可以启动Tomcat服务器测试效果。需要注意,因为当前并没有提及Ajax,所以此时服务器端响应的结果会在一个新页面展示
响应体中的Content-Type¶
Content-Type是一种MIME类型的响应头(如果是上传文件,则请求体中也会有Content-Type)内容,MIME类型告诉客户端从服务器端响应的数据类型,从而使客户端可以正常对响应的数据进行解码
如果使用上面的UserServlet
进行测试会发现,在响应头中不存在这个Content-Type,此时客户端默认就是按照HTML进行解析,而HTML对应的Content-Type就是text/html
例如下面的示例响应头:
Text Only | |
---|---|
1 2 3 4 5 |
|
既然是默认以HTML解析,那么此时也就可以说明为什么前面在写UserServlet类时,在返回的结果字符串中使用了<h1></h1>
标签可以正常被浏览器识别为一级标题
实际上,在一般的响应中都会有指定的Content-Type值,而这个值就是根据响应的文件后缀在Tomcat的web.xml
文件中的<mime-mapping></mime-mapping>
标签中的<extension></extension>
内容进行比对,如果比对成功就使用其<mime-type></mime-type>
的内容作为Content-Type的值。但是在web.xml
文件中无法指定动态资源的后缀名,也就无法确定UserServlet
类的Content-Type值了
但是此时如果服务器端向客户端响应的内容无法使用HTML进行解析,就会出现文件无法正常在客户端呈现,所以在编写代码中要指定Content-Type的值。在UserServlet
类的service
方法中,可以使用响应对象的setHeader()
方法,其有两个参数,第一个参数表示响应头中内容的键,第二个参数表示键对应的值,使用这个方法改写前面的UserServlet
类中的service
方法如下:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
此时再运行Tomcat,在浏览器就可以看到响应头中存在一个Content-Type:
Text Only | |
---|---|
1 2 3 |
|
除了可以使用setHeader()
方法以外,还可以直接使用响应对象的setContentType()
方法,参数传递对应的值即可
url-pattern
的特殊写法¶
前面在配置<servlet-mapping></servlet-mapping>
时提到请求映射地址,对应的标签就是<url-pattern></url-pattern>
,但是当时只是简单的给出了最基本的格式,实际上,这个格式的写法根据需要的匹配模式决定,分为两种匹配模式:
- 精准匹配:每一个字符都必须完全一样,例如:
/servlet1
-
模糊匹配:部分字符一样,其他的字符随意,主要用到通配符
*
,常见有三种形式:/
:表示匹配全部,但是不包括JSP文件/*
:表示匹配全部且包括JSP文件/任意内容/*
:表示精确匹配「任意内容」,但是其斜线后面的内容可以随意*.后缀名
:表示匹配所有满足指定后缀的内容
url-pattern
存在两个特点:
- 一个Servlet可以对应多个
url-pattern
,但是这些url-pattern
不能相同 - 多个Servlet中的
url-pattern
彼此不能相同
尽管url-pattern
可以进行模糊匹配,但是大部分情况下还是使用精准匹配,并且一般情况下一个Servlet会对应一个url-pattern
。因为当前的前后端文件是自己进行编写,所以自己可以自定义,但是在实际开发中,因为前后端分离,所以需要前后端相互约定该路径
使用@WebServlet
注解¶
前面在Servlet初使用时在web.xml
进行了Servlet请求映射地址,但是如果有多个Servlet,这个过程难免会有些繁琐并且如果有太多的Servlet也有可能存在忘记配置某一个Servlet的情况,为了解决这个问题,可以使用@WebServlet
注解
下面是使用注解的方式指定UserServlet
的请求映射路径示例:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
在@WebServlet
中,直接写的值就相当于<url-pattern></url-pattern>
中的内容,格式和规则也是相同的(参考上面的url-pattern
的格式),除了写映射路径,还可以指定Servlet的别名name
值以及loadOnStartup
的值,下面是@WebServlet
的部分源码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
其中:
name
:表示Servlet别名value
和urlPatterns
:表示Servlet请求映射地址,因为一个Servlet可以配置多个映射地址,所以此处是一个String
数组。注意,如果设置了value
值,就不要设置urlPatterns
的值,二者相互排斥,每一次只会有一个生效。之所以会有value是因为value
是存在默认值的,所以使用该注解给value
赋值时,可以不用写value
属性名,并且因为value
被设计成可以不写属性名直接赋值loadonStartup
:表示Servlet是否在项目加载时实例化,如果其值为负数,则表示否,否则表示是,并且此时的值表示初始化的顺序(在下面Servlet声明周期会提到)
Servlet生命周期¶
对象生命周期:应用程序中的对象不仅在空间上有层次结构的关系,在时间上也会因为处于程序运行过程中的不同阶段而表现出不同状态和不同行为
Servlet容器:Servlet对象是Servlet容器创建的,生命周期方法都是由容器(目前我们使用的是Tomcat)调用的。这一点和之前所编写的代码有很大不同,在之后越来越多的对象交给容器或框架来创建,越来越多的方法由容器或框架来调用,开发人员要尽可能多的将精力放在业务逻辑的实现上
Servlet主要的生命周期执行特点
生命周期 | 对应方法 | 执行时机 | 执行次数 |
---|---|---|---|
构造对象 | 构造器 | 第一次请求或者容器启动 | 1 |
初始化 | init() | 构造完毕后 | 1 |
处理服务 | service(HttpServletRequest req,HttpServletResponse resp) | 每次请求 | 多次(具体次数取决于请求次数) |
销毁 | destory() | 容器关闭 | 1 |
可以使用下面的代码测试Servlet的生命周期问题:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
在地址栏中的URL后输入s1
即可访问到ServletLifeCycle
,可以看到第一次访问时打印下面的内容:
Text Only | |
---|---|
1 2 |
|
在进行六次对该页面进行刷新的步骤后,可以看到下面的内容:
Text Only | |
---|---|
1 2 3 4 5 6 |
|
点击结束Tomcat按钮后,可以看到下面的内容:
Text Only | |
---|---|
1 |
|
从上面的过程可以看出初始化方法和销毁方法都只会执行一次,并且执行时机分别在第一次访问页面以及结束Tomcat服务器,只有service
方法在多次请求后会执行多次,与表中的结果对应
根据这个结果可以推出一个结论,一个Servlet类的对象在一次进程中是单例的,所有线程共享,如果当前Servlet类中有一个成员变量,并且在service
方法中对该变量进行修改,这就可能会出现并发请求时的线程安全问题。如果加锁进行同步,那么就会出现延迟问题导致用户体验效果差,所以解决这个问题的关键还是尽可能不在service
方法中对Servlet类中的成员变量进行修改
前面提到初始化方法会在第一次请求页面时进行,那么是否存在一种方式可以使得Servlet在启动Tomcat服务时就创建好?的确存在,就是使用@WebSerlet
中的loadOnStartup
,默认情况下laodOnStartup
的值为-1,表示第一次请求时创建Servlet对象,如果希望在启动Tomcat服务时创建好Servlet对象,那么就建议将该参数的值设置为6及以后
Note
理论上来说,loadOnstartup
的值可以设置为1及以后的数,并且就算是出现冲突,Tomcat也会权衡哪一个对象先创建,但是还是建议不要与默认的laodOnStartup
值冲突,具体可以看web.xml
文件中的<load-on-startup></laod-on-otartup>
中有多少个。在当前Tomcat10中,一共占用了1、2、3、4、5,所以建议将loadOnStartup
参数设置为6及以后
下面提供两种设置loadOnStartup
值的方式:
XML | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
使用其中一种方式进行前面的执行步骤,先启动Tomcat服务而不请求s1
时,当前控制台输出结果如下:
Text Only | |
---|---|
1 |
|
接着请求6次s1,输出如下:
Text Only | |
---|---|
1 2 3 4 5 6 |
|
结束Tomcat服务,输出如下:
Text Only | |
---|---|
1 |
|
需要注意,接下来的问题联系到后面的SpringMVC框架。观察web.xml
文件中<load-on-startup>1</load-on-startup>
,这个意味着别名为default
的Servlet
类(后面称为default-servlet
)在Tomcat启动时第一个被实例化,对应的url-pattern
如下:
XML | |
---|---|
1 2 3 4 |
|
可以看到,其值为/
,这个与请求的资源是动态还是静态有关。如果请求的资源是静态资源,则可能不存在一个动态资源对应的自定义Servlet
类,如果不存在,此时就会走到default-servlet
,而这个Servlet
类就会去找指定的静态资源,接着使用IO流将该静态资源读取到响应对象中,再设置好需要的内容,最后转换成报文发送给客户端。而这里之所以和SpringMVC有关是因为SpringMVC默认会提供一个Servlet
类,此时会把Tomcat提供的default-servlet
给覆盖掉,导致default-servlet
的功能失效,此时再请求静态资源,就会出现找不到静态资源的问题。所以在后面的SpringMVC中,如果没有进行前后端分离,就需要额外配置使default-servlet
重新生效
Servlet继承结构¶
Servlet
接口¶
Servlet
源码
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
接口及方法说明:
Servlet 规范接口,所有的Servlet必须实现
-
public void init(ServletConfig config) throws ServletException;
:初始化方法,容器在构造Servlet
对象后,自动调用的方法,容器负责实例化一个ServletConfig
对象,并在调用该方法时传入,其中ServletConfig
对象可以为Servlet
提供初始化参数 -
public ServletConfig getServletConfig();
:获取ServletConfig
对象的方法,后续可以通过该对象获取Servlet
初始化参数 -
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
:处理请求并做出响应的服务方法,每次请求产生时由容器调用。容器创建一个ServletRequest
对象和ServletResponse
对象,容器在调用service
方法时,传入这两个对象 -
public String getServletInfo();
:获取ServletInfo
信息的方法 -
public void destroy();
:Servlet
实例在销毁之前调用的方法
GenericServlet
抽象类¶
GenericServlet
源码
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
|
解释如下:
GenericServlet
抽象类是对Servlet接口一些固定功能的粗糙实现,以及对service
方法的再次抽象声明,并定义了一些其他相关功能方法:
private transient ServletConfig config;
:初始化配置对象作为属性public GenericServlet() {}
:构造器,为了满足继承而准备public void destroy() {}
:销毁方法的平庸实现(无方法体实现抽象方法)public String getInitParameter(String name)
:获取初始参数的快捷方法public Enumeration<String> getInitParameterNames()
:返回所有初始化参数名的方法public ServletConfig getServletConfig()
:获取初始Servlet初始配置对象ServletConfig
的方法public ServletContext getServletContext()
:获取上下文对象ServletContext
的方法public String getServletInfo()
:获取Servlet
信息的平庸实现public void init(ServletConfig config) throws ServletException()
:初始化方法的实现,并在此调用了init
的重载方法public void init() throws ServletException
:重载init
方法,为了让我们自己定义初始化功能的方法public void log(String msg)
与public void log(String message, Throwable t)
:打印日志的方法及重载public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
:服务方法再次声明public String getServletName()
:获取ServletName
的方法
HttpServlet
抽象类¶
HTTPServlet
部分源码
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
|
解释如下:
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
:对服务方法的实现。在该方法中,将请求和响应对象转换成对应HTTP协议的HttpServletRequest
和HttpServletResponse
对象,调用重载的service
方法public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
:重载的service
方法,被重写的service
方法所调用。在该方法中,通过请求方式判断,调用具体的do***
方法完成请求的处理protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
、protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
、protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
、protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
、protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
、protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
、protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
:对应不同请求方式的处理方法。除了doOptions
和doTrace
方法,其他的do***
方法都在故意响应错误信息
继承结构结论¶
自定义Servlet中,必须要对处理请求的方法进行重写,有两种重写方式:
- 重写
service
方法 - 重写
doGet
或者doPost
方法,或者两种都重写,在调用时,如果doGet
和doPost
逻辑基本一致,只是因为请求方式不同,则可以在doGet
方法或者doPost
方法中调用另外一个即可
实际上,不论是第一种方式还是第二种方式,都没有什么本质的区别,唯一的区别就是第二种方式不会覆盖service
方法中的错误处理,但是目前学习下不会遇到这些问题,也就可以不用深究。在后面使用框架后更不会关心这两种方法,所以使用哪一种方法都可以
ServletConfig
和ServletContext
¶
ServletConfig
对象¶
ServletConfig
为Servlet提供初始配置参数的一种对象,每个Servlet
类都有自己独立唯一的ServletConfig
对象。Tomcat容器会为每个Servlet
类实例化一个ServletConfig
对象,并通过Servlet
生命周期的init
方法传入给Servlet
类对象作为属性
示意图如下:
ServletConfig
是一个接口,定义了如下API:
方法名 | 作用 |
---|---|
getServletName() | 获取<servlet-name></servlet-name> 定义的Servlet名称 |
getServletContext() | 获取ServletContext 对象 |
getInitParameter() | 获取配置Servlet时设置的初始化参数,根据名字获取值 |
getInitParameterNames() | 获取所有初始化参数名组成的Enumeration 对象 |
源码如下:
Java | |
---|---|
1 2 3 4 5 6 7 8 |
|
Note
Enumeration是旧式的迭代器,使用方式和现在的迭代器类似,其中有两个方法:
hasMoreElements()
:判断是否还有下一个元素nextElement()
:获取下一个元素
为每一个Servlet类对象提供初始化参数有两种方式,一种是在web.xml
文件中配置,另外一种就是使用@WebServlet
注解:
XML | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
以注解的方式进行测试,测试代码如下:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
运行Tomcat服务器请求s1可以发现控制台输出如下:
Text Only | |
---|---|
1 2 3 |
|
ServletContext
对象¶
ServletContext
对象有称呼为上下文对象,或者叫应用域对象(后面会介绍域对象)。Tomcat容器会为每个APP创建一个独立的唯一的ServletContext
对象。因为该对象为所有的Servlet所共享,所以其可以为所有的Servlet提供初始配置参数
ServletConfig
对象和ServletContext
对象的区别如下图所示:
ServletContext
对象获取初始化参数的方法和ServletConfig
对象的方法一致,此处不再赘述
因为ServletConfig
对象是每一个Servlet
类所独有的,所以在配置ServletConfig
对象之前需要存在Servlet
类,但是ServletContext
对象不同,它不依赖认为一个Servlet
类,所以在配置时通常在web.xml
中配置,并且不放置在任何<servlet></servlet>
中:
XML | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
使用下面的Servlet类进行测试:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
运行Tomcat并请求s1后可以看到控制台输出如下:
Text Only | |
---|---|
1 2 3 |
|
对于除了上面提到的获取键值对的方法外,ServletContext
对象还有其他的API:
getRealPath()
:用于获取资源的真实路径,也就是构建的资源在当前电脑的硬盘路径。在副本Tomcat中的conf->Catalina->localhost
中的xml
文件可以看到有个Context
标签,其docBase
值即为该方法的值。该方法可以传递一个参数,表示指定该目录下的子目录或者子文件getContextPath()
:用于获取资源的上下文路径,也就是访问指定资源时的路径。在与上面同一位置的xml
文件中的Context
标签的path
值即为该方法的值- 域对象API:在下面讲解域对象时具体说明
Note
getRealPath()
方法的一个作用就是获取到构建的资源所处的位置,通过该位置找到其中的某一个子文件夹,如果直接写成固定的位置,那么换一个设备位置可能不一样,而使用getRealPath()
方法就可以确保不包括子文件夹的父级路径是动态获取的
例如下面的代码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
域对象¶
域对象:即存在作用范围的对象,这些对象一般被用于存储数据以及获取数据,而其中的作用范围就是「域」
在WebAPP中,存在三种域对象:
- 应用域
- 会话域
- 请求域
前面提到的ServletContext
对象就是应用域对象,应用域是WebAPP中最大的域,所以其对象的作用范围也最广。一般来说,应用域对象可以在本Web应用中进行数据共享和传递
三大域对象都有下面的三种API:
API | 功能解释 |
---|---|
void setAttribute(String key,Object value); | 向域中存储/修改数据 |
Object getAttribute(String key); | 获得域中的数据 |
void removeAttribute(String key); | 移除域中的数据 |
例如,当前应用中存在两个Servlet
,分别是Servlet1
和Servlet2
。二者按下面的顺序执行:
Servlet1
向ServletContext
对象中存储数据Servlet1
取出ServletContext
对象中的数据Servlet2
从ServletContext
对象中读取数据
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
启动Tomcat服务器后查看控制台输出如下:
-
先访问
Servlet1
:Text Only 1 2
key1:value1 key2:value2
-
再访问
Servlet2
:Text Only 1 2
key1:value1 key2:value2
HttpServletRequest
对象常见API¶
HttpServletRequest
是一个接口,其父接口是ServletRequest
,其对象是Tomcat将请求报文转换封装而来的对象,在Tomcat调用service
方法时传入。HttpServletRequest
代表客户端发来的请求,所有请求中的信息都可以通过该对象获得
其对象常见的API如下:
- 获取请求行信息相关(请求方式,请求的URL和协议及版本)
API | 功能解释 |
---|---|
StringBuffer getRequestURL(); | 获取客户端请求的URL |
String getRequestURI(); | 获取客户端请求项目中的具体资源 |
int getServerPort(); | 获取客户端发送请求时的端口 |
int getLocalPort(); | 获取本应用所在容器的端口 |
int getRemotePort(); | 获取客户端程序的端口 |
String getScheme(); | 获取请求协议 |
String getProtocol(); | 获取请求协议及版本号 |
String getMethod(); | 获取请求方式 |
Note
在上面的方法中,需要注意下面的两点:
-
- URI(Uniform Resource Identifier,统一资源标识符)是不包括协议、IP和端口的资源路径
- URL(Uniform Resource Locator,统一资源定位器)是包括协议、IP和端口的全路径
URI和URL的区别:
可以理解为URL是URI的一种扩展,其具有URI的内容(资源路径),也有URI不具有的内容(协议、IP和端口)。例如:
http://127.0.0.1:8080/demo1/servlet1
,其中URI为demo1/servlet1
,URL为http://127.0.0.1:8080/demo1/servlet1
-
「客户端发送请求时的端口」和「本应用所在容器的端口」的区别:
所谓「客户端发送请求时的端口」就是客户端发送请求时请求的服务器所在的端口,如果客户端和服务器端没有代理服务器,那么「客户端发送请求时的端口」和「本应用所在容器的端口」是一致的。但是如果存在代理服务器,则「客户端发送请求时的端口」就是代理服务器的端口号,此时可能就与「本应用所在容器的端口」不同
- 获得请求头信息相关
API | 功能解释 |
---|---|
String getHeader(String headerName); | 根据头名称获取请求头 |
Enumeration<String> getHeaderNames(); | 获取所有的请求头名字 |
String getContentType(); | 获取Content-Type 请求头 |
- 获得请求参数相关
API | 功能解释 |
---|---|
String getParameter(String parameterName); | 根据请求参数名获取请求单个参数值 |
String[] getParameterValues(String parameterName); | 根据请求参数名获取请求多个参数值数组 |
Enumeration<String> getParameterNames(); | 获取所有请求参数名 |
Map<String, String[]> getParameterMap() ;` | 获取所有请求参数的键值对集合 |
BufferedReader getReader() throws IOException; | 获取读取请求体的字符输入流 |
ServletInputStream getInputStream() throws IOException; | 获取读取请求体的字节输入流 |
int getContentLength(); | 获得请求体长度的字节数 |
- 其他API
API | 功能解释 |
---|---|
String getServletPath(); | 获取请求的Servlet 的映射路径 |
ServletContext getServletContext(); | 获取ServletContext 对象 |
Cookie[] getCookies(); | 获取请求中的所有cookie |
HttpSession getSession(); | 获取Session 对象 |
void setCharacterEncoding(String encoding); | 设置请求体字符集 |
HttpServletResponse
对象常见API¶
HttpServletResponse
是一个接口,其父接口是ServletResponse
。与HttpServletRequest
对象一样,是Tomcat将请求报文转换封装而来的对象,在Tomcat调用service
方法时传入。HttpServletResponse
代表对客户端的响应该对象会被转换成响应的报文发送给客户端,通过该对象可以设置响应信息
其对象常见的API如下:
- 设置响应行相关
API | 功能解释 |
---|---|
void setStatus(int code); | 设置响应状态码 |
- 设置响应头相关
API | 功能解释 |
---|---|
void setHeader(String headerName, String headerValue); | 设置/修改响应头键值对 |
void setContentType(String contentType); | 设置Content-Type 响应头及响应字符集(设置MIME类型) |
- 设置响应体相关
API | 功能解释 |
---|---|
PrintWriter getWriter() throws IOException; | 获得向响应体放入信息的字符输出流 |
ServletOutputStream getOutputStream() throws IOException; | 获得向响应体放入信息的字节输出流 |
void setContentLength(int length); | 设置响应体的字节长度,其实就是在设置Content-Length 响应头 |
- 其他API
API | 功能解释 |
---|---|
void sendError(int code, String message) throws IOException; | 向客户端响应错误信息的方法,需要指定响应码和响应信息 |
void addCookie(Cookie cookie); | 向响应体中增加cookie |
void setCharacterEncoding(String encoding); | 设置响应体字符集 |
请求转发和响应重定向¶
请求转发和响应重定向是Web应用中间接访问项目资源的两种手段,也是Servlet
控制页面跳转的两种手段。其中,除了使用请求转发外,其他方式都不可以访问WEB-INF
文件夹中的文件
请求转发通过HttpServletRequest
实现,响应重定向通过HttpServletResponse
实现
请求转发¶
请求转发逻辑图:
请求转发的特点:
- 请求转发通过
HttpServletRequest
对象获取请求转发器实现 - 请求转发是服务器内部的行为,对客户端是屏蔽的,所以客户端只发送了一次请求,客户端地址栏不变
- 服务端只产生了一对请求和响应对象,这一对请求和响应对象会继续传递给下一个资源。因为全程只有一个
HttpServletRequset
对象,所以请求参数可以传递,请求域中的数据也可以传递 - 请求转发可以转发给其他
Servlet
动态资源,也可以转发给一些静态资源以实现页面跳转,但是请求转发不能转发到本项目以外的外部资源 - 请求转发可以转发给
WEB-INF
下受保护的资源
使用请求转发时需要使用到下面的方法:
- 使用
HttpServletRequest
对象的方法getRequestDispatcher()
获取请求转发器,参数传递请求转发的资源路径 - 通过请求转发器调用
forward()
方法,参数传递HttpServletRequest
对象和HttpServletResponse
对象
例如下面的代码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
Java | |
---|---|
1 2 3 4 5 6 7 |
|
配置Tomcat后启动可以看到控制台输出如下:
Text Only | |
---|---|
1 2 |
|
请求转发访问WEB-INF
下的资源:
当前在WEB-INF
下有一个test.html
文件,其内容如下:
HTML | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
对应的Servlet1
代码如下:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
配置Tomcat后启动可以看到控制台输出和网页显示内容如下:
Text Only | |
---|---|
1 |
|
响应重定向¶
响应重定向逻辑图:
响应重定向的特点:
- 响应重定向通过
HttpServletResponse
对象的sendRedirect()
方法实现,该方法传递目标资源路径 - 响应重定向是服务端通过302响应码和路径,告诉客户端自己去找其他资源,这个是客户端在服务端提示下的行为
- 客户端至少发送了两次请求,客户端地址栏是要变化的,并且因为服务端产生了多对
HttpServletRequset
对象和HttpServletResponse
对象,所以请求和响应对象不会传递给下一个资源,因此请求参数和请求域中的数据也不可以传递 - 重定向可以是其他
Servlet
动态资源,也可以是一些静态资源以实现页面跳转,并且重定向可以到本项目以外的外部资源 - 重定向不可以到给
WEB-INF
下受保护的资源
例如下面的代码:
Java | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
Java | |
---|---|
1 2 3 4 5 6 7 |
|
配置Tomcat后启动可以看到控制台输出如下:
Text Only | |
---|---|
1 2 |
|