目录
1. Web相关概念
1.1 软件架构
常见软件架构分为以下两类:
- C/S:客户端/服务器端
- B/S:浏览器/服务器端
1.2 资源分类
web资源分为静态资源和动态资源两种。
静态资源:所有用户访问后,得到的结果都是一样的,称为静态资源。静态资源可以直接被浏览器解析。如: html,css,JavaScript.......
动态资源:每个用户访问相同资源后,得到的结果可能不一样。称为动态资源。动态资源被访问后,需要先转换为静态资源,在返回给浏览器。如:servlet/jsp,php,asp....

1.3 网络通信三要素
- IP:电子设备(计算机)在网络中的唯一标识。
- 端口:应用程序在计算机中的唯一标识。 0~65536
- 传输协议:规定了数据传输的规则。常用基础协议:
- tcp:安全协议,三次握手。 速度稍慢。
- udp:不安全协议。 速度快。
2. Tomcat
2.1 web服务器软件概念
服务器:安装了服务器软件的计算机。
服务器软件:接收用户的请求,处理请求,做出响应的软件。
web服务器软件:接收用户的请求,处理请求,做出响应。在web服务器软件中,可以部署web项目,让用户通过浏览器来访问这些项目。
2.2 常见的java相关的web服务器软件
- webLogic:oracle公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
- webSphere:IBM公司,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
- JBOSS:JBOSS公司的,大型的JavaEE服务器,支持所有的JavaEE规范,收费的。
- Tomcat:Apache基金组织,中小型的JavaEE服务器,仅仅支持少量的JavaEE规范servlet/jsp。开源的,免费的。
而我们主要学习的就是tomcat。
JavaEE:是Java语言在企业级开发中使用的技术规范的总和,一共规定了13项大的规范。
2.3 Tomcat
2.3.1 Tomcat的下载、安装与卸载
tomcat的地址地址:http://tomcat.apache.org/

下载下来后将压缩包解压即可,就已经安装完成了。
(注意:安装目录(即存放Tomcat解压缩后的目录文件夹)不要有中文和空格。)

如果要卸载tomcat的话,直接删除安装路径就可以了。
2.3.2 Tomcat的启动
找到bin目录下的startup.bat,双击运行该文件即可。

会出现一个黑框,打印着日志

接着在浏览器输入http://localhost:8080即可访问了
在使用过程中可以遇到问题:
- (1)黑窗口一闪而过
- 原因:没有正确配置JAVA_HOME环境变量
- 解决方案:正确配置JAVA_HOME环境变量,百度解决。
- (2)启动报错端口被占用
- 原因:80880端口被占用
- 解决:关闭掉被占用的端口。有以下两种解决方案:
- ①找到占用的端口号,并且找到对应的进程,杀死该进程。通过netstat -ano在DOS窗口找到8080被占用的端口。
- ②修改自身的端口号,通过conf/server.xml配置文件
注:一般会将tomcat的默认端口号修改为80。80端口号是http协议的默认端口号。
2.3.3 Tomcat的关闭
关闭tomcat分为正常关闭和强制关闭。
- 正常关闭:
- 找到bin/shutdown.bat,双击运行即可。
- 使用快捷键Ctrl+C
- 强制关闭:
- 点击启动窗口的×
2.3.4 Tomcat的配置
Tomcat的目录结构如下:

部署配置项目的方式有如下三种:
1. 直接将项目放到webapps目录下即可。

然后通过/Demo3_war这样的方式访问,其中/Demo3_war是项目的访问路径,也是该项目的根目录文件夹名称。

除了上面的,还可以简化部署,将项目打成一个war包,再将war包放置到webapps目录下,war包会自动解压。

2. 配置conf/server.xml文件(这种配置很不安全,可能弄坏整个tomcat的配置)
在<Host>标签中作如下配置:

其中D盘的hello目录下有如下文件作为测试:

在浏览器进行访问:

注:不推荐使用,不便于管理项目。
3. 在conf\Catalina\localhost创建任意名称的xml文件

重启tomcat,浏览器进行访问

下面介绍下Java动态项目的目录结构:
- 项目的根目录
- WEB-INF目录:
- web.xml:web项目的核心配置文件
- classes目录:放置字节码文件的目录
- lib目录:放置依赖的jar包
- WEB-INF目录:
2.3.5 将Tomcat集成到IDEA中
使用IDEA创建一个JavaEE项目,并部署项目。

点击“New”集成tomcat


集成成功

然后点击“Next”创建web项目

如下创建项目完成

点击绿色箭头运行,会自动打开浏览器

项目集成成功。
下面说下一些基本使用:
- 热部署静态资源

即在web下创建一些如html文件,在html更新内容等,都不需要再重启tomcat,直接刷新浏览器就能访问了。
- 配置虚拟路径

配置后,浏览器输入:http://localhost:8080/hello/才能访问
如果不配置,浏览器通过http://localhost:8080/就能访问

3.Servlet入门
3.1 Servlet概念
Servlet是运行在服务器端的小程序,servlet全称是server applet。
实际上Servlet就是一个接口,定义了Java类被浏览器访问到(tomcat识别)的规则。
我们(程序员)使用的时候,就是自定义一个类来实现Servlet接口,复写方法。
3.2 快速入门
步骤:
- (1)使用IDEA创建JavaEE项目。
- (2)自定义一个类,实现Servlet接口。
- (3)实现接口中的抽象方法,主要是在service()方法中输出。
- (4)在web.xml中配置Servlet
第一步:创建一个JavaEE项目

第二步:自定义一个类,实现Servlet接口
public class HelloServlet 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 {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
第三步:实现接口中的抽象方法,主要是在service()方法中输出。

第四步:在web.xml中配置Servlet
<!--配置Servlet -->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.demo.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
第五步:测试
运行tomcat,浏览器输入:http://localhost:8080/hello控制台查看打印结果

发现service()方法被执行。
3.3 Servlet执行原理
执行原理如下图:
- 1. 当服务器接受到客户端浏览器的请求后,会解析请求URL路径,获取访问的Servlet的资源路径
- 2. 查找web.xml文件,是否有对应的<url-pattern>标签体内容。
- 3. 如果有,则在找到对应的<servlet-class>全类名
- 4. tomcat会将全类名所代表的字节码文件加载进内存,并且创建其对象
- 5. 调用其方法,这里调用了service()方法。

3.4 Servlet的生命周期
如下图:


(1)被创建:执行init()方法,只执行一次。
①概述
Servlet什么时候被创建?即第一次被访问时,Servlet才会被创建。
即只有访问如上http://localhost:8080/hello才会执行init()方法和service()方法

但是,可以手动配置执行Servlet的创建时机。
②手动配置创建Servlet的时机
配置方法如下(在web.xml的<servlet>标签下配置):
- 第一次访问时,创建Servlet,那么设置<load-on-startup>值为负数,默认值为-1.
下面的配置就会在第一次访问时创建Servlet,同不配置产生一样的效果。

- 在服务器启动时,创建Servlet,那么设置<load-on-startup>值为0或正整数
使用如下配置时,在启动服务器后就会执行init()方法,创建Servlet,控制台也会输出"init..."

③可能存在的线程安全问题
Servlet的init()方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的。
多个用户同时访问时,如果Servlet中定义了成员变量,则可能存在线程安全问题。
解决:尽量不要在Servlet中定义成员变量。即使定义了成员变量,也不要对修改值。

(2)提供服务:执行service()方法,执行多次。
每次访问Servlet时,service()方法都会被调用一次。
即访问如上http://localhost:8080/hello都会执行service()方法,就可以看到控制台不断打印"service..."

(3)被销毁:执行destroy()方法,只执行一次。
- 该destroy()方法是Servlet被销毁时才执行。
- 什么时候Servlet被销毁呢?当服务器关闭时,Servlet被消耗。
- 但注意,只有服务器正常关闭时,才会执行destroy方法。
- destroy方法在Servlet被销毁之前执行,一般用于释放资源

除了上面三个关于servlet生命周期的方法之外,实现Servlet接口还有两个方法getServletConfig()和getServletInfo()。
- getServletConfig()方法获取ServletConfig对象,即Servlet的配置对象
- getServletInfo()方法获取servlet的一些信息,关于版本、作者的信息

3.5 Servlet3.0
(1)概述
Servlet3.0比较显著的好处是支持注解配置,即不需要配置web.xml文件了。

其他特性请参考:Servlet 3.0 新特性详解
(2)使用步骤
使用步骤如下:
- 创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml
- 定义一个类,实现Servlet接口
- 复写方法
- 在类上使用@WebServlet注解,进行配置
第一步:创建JavaEE项目,选择Servlet的版本3.0以上,可以不创建web.xml

第二步:定义一个类,实现Servlet接口
public class HelloServlet implements Servlet {
}
第三步:复写方法
public class HelloServlet implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init...");
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) {
System.out.println("service...");
}
@Override
public void destroy() {
System.out.println("destroy...");
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
}
第四步:在类上使用@WebServlet注解,进行配置

其中该注解
@WebServlet(urlPatterns = "/hello")
// 可以简写为如下形式:
// @WebServlet("资源路径")
@WebServlet("/hello")
等价于
<!--配置Servlet -->
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.demo.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
查看@WebServlet注解的源代码,如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
String name() default "";//相当于<Servlet-name>
String[] value() default {};//代表urlPatterns()属性配置
String[] urlPatterns() default {};//相当于<url-pattern>
int loadOnStartup() default -1;//相当于<load-on-startup>
WebInitParam[] initParams() default {};
boolean asyncSupported() default false;
String smallIcon() default "";
String largeIcon() default "";
String description() default "";
String displayName() default "";
}
3.6 Servlet的体系结构
为什么会说到Servlet体系结构呢?实现一个Servlet接口,但实际使用的只有service()方法,其他方法基本上用不到,还不能删除。

(1)Servlet
该类要实现五个方法,但常用的只有一个service()方法
@WebServlet(urlPatterns = "/hello")
public class HelloServlet 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 {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
(2)GenericServlet
该类将Servlet接口中其他的方法做了默认空实现,只将service()方法作为抽象。
因此将来定义Servlet类时,可以继承GenericServlet,实现service()方法即可。
@WebServlet("/hello2")
public class HelloServlet2 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
}
}
(3)HttpServlet
这才是最常用的实现类,屏蔽了对请求方式的判断处理。
对http协议的一种封装,简化操作。
如图:

实现上查看HttpServlet的源代码的service()方法,正是对请求方法getMethod()的封装。

一般自定义类使用该类的步骤如下:
- 定义类继承HttpServlet
- 复写doGet/doPost方法
public class HelloServlet3 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
3.7 Servlet相关配置
(1)urlPartten:Servlet访问路径
由下图可以看出<url-partten>对应的是Servlet访问路径

使用注解后对应的就是urlPartten参数,该参数指向Servlet访问路径。

注意:
- 一个Servlet可以定义多个访问路径 : @WebServlet({"/d4","/dd4","/ddd4"})
- 路径定义规则:
- /xxx:路径匹配。例如"/login"等
- /xxx/xxx:多层路径,目录结构。/xxx/*表示匹配xxx目录下的任意Servlet。例如"/user/login"等。
- *.do:扩展名匹配,不要加"/",否则会报错
3.8 IDEA与Tomcat的相关配置
1、IDEA会为每一个tomcat部署的项目单独建立一份配置文件
运行一个Servlet项目后,可以在控制台看到如下的输出日志:

注意Using CATALINA_BASE,复制它对应的路径,在本地资源管理器打开:

打开conf目录,里面就是配置文件

如server.xml配置文件中可以修改端口、虚拟目录等。

但我们同IDEA集成使用后,不需要再在这个配置文件中修改,只需要在IDE界面中修改就会同步到配置文件的修改中:

发现配置文件也同步修改了

如果要查看项目的所在目录,可以在/conf/Catalina/localhost下查看这个xml配置文件

打开查看,就是项目实际所在的目录

再将这个目录在资源管理器中打开,就是本项目

工作空间项目 和 tomcat部署的web项目
- tomcat真正访问的是“tomcat部署的web项目”,"tomcat部署的web项目"对应着"工作空间项目" 的web目录下的所有资源
- WEB-INF目录下的资源不能被浏览器直接访问。


4. HTTP
4.1 概念
HTTP全称是Hyper Text Transfer Protocol,即是超文本传输协议。
什么是传输协议?即定义了客户端和服务器端通信时,发送数据的格式。
特点:
- 基于TCP/IP的高级协议
- 默认端口号:80
- 基于请求/响应模型的:一次请求对应一次响应
- 无状态的:每次请求之间相互独立,不能交互数据

历史版本:
- 1.0:每一次请求响应都会建立新的连接
- 1.1:复用连接
4.2 请求消息数据格式
通过浏览器向Servlet发送一个请求,然后浏览器F12查看,可以看到请求头和响应头。

将GET请求头原始信息复制下来,如下:
GET /hello HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: Webstorm-bbf7ba1a=9f630819-8a4f-40ea-8164-7a71918ee9aa; Idea-d1852ee0=946448e7-234a-4caa-83bb-38b847d8fb58; JSESSIONID=ADE0A02EA8BCCC03020517D711FFF2A9
Upgrade-Insecure-Requests: 1
将POST请求头原始信息复制下来,如下:
POST /hello HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 13
Origin: http://localhost:8080
Connection: keep-alive
Referer: http://localhost:8080/postSubmit.jsp
Cookie: Webstorm-bbf7ba1a=9f630819-8a4f-40ea-8164-7a71918ee9aa; Idea-d1852ee0=946448e7-234a-4caa-83bb-38b847d8fb58; JSESSIONID=86AE0FB0BDD6142DDD4B6A3AD2438374
Upgrade-Insecure-Requests: 1
请求消息是客户端发送给服务器端的数据,请求消息的数据格式分为:
- 请求行
- 请求头
- 请求空行
- 请求体(正文)
4.2.1 请求行
请求行定义了请求方式、请求URL与请求协议/版本号相关信息。
GET /hello HTTP/1.1

关于请求方式,HTTP协议有7种请求方式,常用的有如下两种:
- GET
- 1. 请求参数在请求行中,在url后。如localhost:8080/hello?username=zhangsan
- 2. 请求的url长度有限制的
- 3. 不太安全
- POST
- 1. 请求参数在请求体中
- 2. 请求的url长度没有限制的
- 3. 相对安全
4.2.2 请求头
请求头定义了客户端浏览器要告诉服务器的一些信息。
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: Webstorm-bbf7ba1a=9f630819-8a4f-40ea-8164-7a71918ee9aa; Idea-d1852ee0=946448e7-234a-4caa-83bb-38b847d8fb58; JSESSIONID=ADE0A02EA8BCCC03020517D711FFF2A9
Upgrade-Insecure-Requests: 1
常见的请求头:
- User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息,是浏览器版本信息
- 作用:可以在服务器端获取该头的信息,解决浏览器的兼容性问题
- Referer:告诉服务器,我(当前请求)从哪里来?例如: http://localhost/login.html
- 作用:防盗链;统计工作。
4.2.3 请求空行
空行,就是用于分割POST请求的请求头,和请求体的。
4.2.4 请求体(正文)
GET方式是没有请求体的。
封装POST请求消息的请求参数的。

4.3 响应消息数据格式
将响应头数据信息复制下来,如下:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Length: 0
Date: Thu, 10 Dec 2020 02:20:53 GMT
有响应数据的情况下会有数据

响应消息是服务器端发送给客户端的数据,数据格式如下:
- 响应行
- 响应头
- 响应空行
- 响应体:传输的数据
4.3.1 响应行
HTTP/1.1 200 OK

响应行的组成:协议/版本 响应状态码 状态码描述
响应状态码:服务器告诉客户端浏览器本次请求和响应的一个状态。
- 状态码都是3位数字
- 状态码的分类:
- 1xx:服务器就收客户端消息,但没有接受完成,等待一段时间后,发送1xx多状态码
- 2xx:成功。代表:200
- 3xx:重定向。代表:302(重定向),304(访问缓存)
- 4xx:客户端错误。代表如下:
- 404(请求路径没有对应的资源)
- 405:请求方式没有对应的doXxx方法
- 5xx:服务器端错误。代表:500(服务器内部出现异常)
4.3.2 响应头
Server: Apache-Coyote/1.1
Content-Length: 0
Date: Thu, 10 Dec 2020 02:20:53 GMT

常见的响应头:
- Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式。
- Content-disposition:服务器告诉客户端以什么格式打开响应体数据。可选择的值有如下两种:
- in-line:默认值,在当前页面内打开
- attachment;filename=xxx:以附件形式打开响应体。文件下载
4.3.3 响应空行
4.3.4 响应体
响应传输的数据,有字符串也有二进制数据,都会由浏览器解析。
(建议:使用抓包工具查看请求信息与响应信息,学习更为直观)
5. Servlet的Request对象
5.1 request对象和request对象的原理
request和response对象是由服务器创建的。我们来使用它们。
request对象是来获取请求消息,response对象是来设置响应消息。

5.2 Request对象的继承体系结构
ServletRequest -- 接口
| 继承
HttpServletRequest -- 接口
| 实现
org.apache.catalina.connector.RequestFacade 类(tomcat编写的,可以在tomcat的源码中看到)

![]()
5.3 Request对象的功能
5.3.1 获取请求消息数据
5.3.1.1 获取请求行数据
即要获取如下数据:
GET /demo/hello?username=lisi HTTP/1.1
要获取以上请求行数据,Request对象提供了以下方法:
- 1. 获取请求方式:GET
- String getMethod()
- 2. 获取虚拟目录:/demo
- String getContextPath()
- 3. 获取Servlet路径: /hello
- String getServletPath()
- 4. 获取get方式请求参数:username=lisi
- String getQueryString()
- 5. 获取请求URI:/demo/hello
- String getRequestURI() —— /demo/hello
- StringBuffer getRequestURL() —— http://localhost:8080/demo/hello
- 6. 获取协议及版本:HTTP/1.1
- String getProtocol()
- 7. 获取客户机的IP地址:127.0.0.1
- String getRemoteAddr()
代码实例如下:
@WebServlet("/hello")
public class HelloServlet1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 获取请求方式:GET
String method = request.getMethod();
System.out.println(method);
System.out.println("-----------------------------------------");
// 2. 获取虚拟目录:/demo
String contextPath = request.getContextPath();
System.out.println(contextPath);
System.out.println("-----------------------------------------");
// 3. 获取Servlet路径: /hello
String servletPath = request.getServletPath();
System.out.println(servletPath);
System.out.println("-----------------------------------------");
// 4. 获取get方式请求参数:username=lisi
String queryString = request.getQueryString();
System.out.println(queryString);
System.out.println("-----------------------------------------");
// 5. 获取请求URI:/demo/hello
String requestURI = request.getRequestURI();
System.out.println(requestURI);
StringBuffer requestURL = request.getRequestURL();
System.out.println(requestURL);
System.out.println("-----------------------------------------");
// 6. 获取协议及版本:HTTP/1.1
String protocol = request.getProtocol();
System.out.println(protocol);
System.out.println("-----------------------------------------");
// 7. 获取客户机的IP地址:127.0.0.1
String remoteAddr = request.getRemoteAddr();
System.out.println(remoteAddr);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
/**
* 打印结果:
*
* GET
* -----------------------------------------
* /demo
* -----------------------------------------
* /hello
* -----------------------------------------
* username=lisi
* -----------------------------------------
* /demo/hello
* http://localhost:8080/demo/hello
* -----------------------------------------
* HTTP/1.1
* -----------------------------------------
* 127.0.0.1
*/
在浏览器输入http://localhost:8080/demo/hello?username=lisi查看控制台打印结果
5.3.1.2 获取请求头数据
如果要获取这样的请求头数据:
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0
可以有如下方法:
- String getHeader(String name):通过请求头的名称获取请求头的值,name参数不区分大小写
- Enumeration<String> getHeaderNames():获取所有的请求头名称
代码实例:可以通过判断user-agent来判断使用的浏览器从而解决浏览器的兼容问题。
@WebServlet("/hello2")
public class HelloServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// String getHeader(String name):通过请求头的名称获取请求头的值,name参数不区分大小写
String host = request.getHeader("Host");
System.out.println(host);
String userAgent = request.getHeader("user-agent");
System.out.println(userAgent);
System.out.println("-------------------------");
// Enumeration<String> getHeaderNames():获取所有的请求头名称
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
System.out.println(headerNames.nextElement());
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
/**
* 打印结果:
* localhost:8080
* Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
* -------------------------
* host
* connection
* upgrade-insecure-requests
* user-agent
* sec-fetch-dest
* accept
* sec-fetch-site
* sec-fetch-mode
* sec-fetch-user
* accept-encoding
* accept-language
* cookie
*/
浏览器输入http://localhost:8080/demo/hello2然后在控制台查看打印结果
5.3.1.3 获取请求体数据
请求体:只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数。
因此得到请求体数据的步骤如下:
- 1. 获取流对象
- BufferedReader getReader():获取字符输入流,只能操作字符数据
- ServletInputStream getInputStream():获取字节输入流,可以操作所有类型数据
- 2. 再从流对象中拿数据
因此在web下创建一个post.jsp来测试.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/hello3" method="post">
<input type="text" name="username">
<input type="submit" value="提交">
</form>
</body>
</html>
然后在Servlet中使用getReader()方法读取
@WebServlet("/hello3")
public class HelloServlet3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取字符流
BufferedReader br = request.getReader();
//2.读取数据
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
getInputStream()后面再举例说明。
5.4 Request对象的其他功能
5.4.1 获取请求参数的通用方式
上面的方式获取某个参数很麻烦,所以有以下通用获取请求参数的方式。不论get还是post请求方式都可以使用下列方法来获取请求参数。
- 1. String getParameter(String name):根据参数名称获取参数值
- 例如: username=zs&password=123
- 2. String[ ] getParameterValues(String name):根据参数名称获取参数值的数组
- 例如:hobby=xx&hobby=game
- 3. Enumeration<String> getParameterNames():获取所有请求的参数名称
- 4. Map<String,String[]> getParameterMap():获取所有参数的map集合
在POST提交中可能遇到中文问题:
- get方式:tomcat 8 已经将get方式乱码问题解决了
- post方式:会乱码
- 解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");。注意,这里设置成utf-8其实是跟前端页面设置的编码是utf8有关,必须一致。
实例代码:
demo4.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/demo/hello4" method="post">
<input type="text" placeholder="请输入用户名" name="username"><br>
<input type="text" placeholder="请输入密码" name="password"><br>
<input type="checkbox" name="hobby" value="game">游戏
<input type="checkbox" name="hobby" value="study">学习
<br>
<input type="submit" value="注册">
</form>
</body>
</html>
HelloServlet4.java
@WebServlet("/HelloServlet4")
public class HelloServlet4 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//post 获取请求参数
//根据参数名称获取参数值
String username = request.getParameter("username");
/* System.out.println("post");
System.out.println(username);*/
//根据参数名称获取参数值的数组
String[] hobbies = request.getParameterValues("hobby");
/*for (String hobby : hobbies) {
System.out.println(hobby);
}*/
//获取所有请求的参数名称
Enumeration<String> parameterNames = request.getParameterNames();
/*while(parameterNames.hasMoreElements()){
String name = parameterNames.nextElement();
System.out.println(name);
String value = request.getParameter(name);
System.out.println(value);
System.out.println("----------------");
}*/
// 获取所有参数的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
//遍历
Set<String> keyset = parameterMap.keySet();
for (String name : keyset) {
//获取键获取值
String[] values = parameterMap.get(name);
System.out.println(name);
for (String value : values) {
System.out.println(value);
}
System.out.println("-----------------");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//get 获取请求参数
/*
//根据参数名称获取参数值
String username = request.getParameter("username");
System.out.println("get");
System.out.println(username);*/
this.doPost(request, response);
}
}
因为doPost()和doGet()方法中使用的是通用请求参数方式,都可以使用,因此只需要在一个方法体内写逻辑处理,另一个方法直接调用写了逻辑处理的方法即可。
5.4.2 请求转发
(注意:同请求重定向比较学习)
请求转发是一种在服务器内部的资源跳转方式。
是通过Request来进行的,从一个Servlet跳转到另一个Servlet。

请求转发的步骤:
- 1. 通过request对象获取请求转发器对象:RequestDispatcher getRequestDispatcher(String path)
- 2. 使用RequestDispatcher对象来进行转发:forward(ServletRequest request, ServletResponse response)
// 请求转发的第一种写法
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/hello5_2");
requestDispatcher.forward(request, response);
// 请求转发的第二种写法
request.getRequestDispatcher("/hello5_2").forward(request, response);
请求转发的特点:
- 1. 浏览器地址栏路径不发生变化
- 2. 只能转发到当前服务器内部资源中。即无法转发到百度等外部网站中
- 3. 转发是一次请求
实例代码:
HelloServlet5_1.java
@WebServlet("/hello5_1")
public class HelloServlet5_1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("HelloServlet5_1...");
// // 请求转发的第一种写法
// RequestDispatcher requestDispatcher = request.getRequestDispatcher("/hello5_2");
// requestDispatcher.forward(request, response);
//存储数据到request域中
request.setAttribute("msg", "hello");
// 请求转发的第二种写法
request.getRequestDispatcher("/hello5_2").forward(request, response);
// 无法转发到当前服务器之外的资源
// request.getRequestDispatcher("http://www.baidu.com").forward(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
HelloServlet5_2.java
@WebServlet("/hello5_2")
public class HelloServlet5_2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取数据
Object msg = request.getAttribute("msg");
System.out.println(msg);
System.out.println("HelloServlet5_2...");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
5.4.3 共享数据
可以在request作用域内共享数据,即从一个Servlet得到请求转发的多个Servlet内都可以共享request域内的数据。
- 域对象:一个有作用范围的对象,可以在范围内共享数据
- request域:代表一次请求的范围,一般用于请求转发的多个资源中共享数据,即从一个Servlet得到请求转发的多个Servlet。
- 方法:
- 1. void setAttribute(String name,Object obj):存储数据
- 2. Object getAttitude(String name):通过键获取值
- 3. void removeAttribute(String name):通过键移除键值对
(实例代码参考上一节)
5.4.4 获取ServletContext
- 方法:ServletContext getServletContext()
该方法获取到的ServletContext对象后面会详细说明。
5.5 案例:用户登录
5.5.1 用户登录案例需求
- 1.编写login.html登录页面。username & password 两个输入框
- 2.使用Druid数据库连接池技术,操作mysql,day14数据库中user表
- 3.使用JdbcTemplate技术封装JDBC
- 4.登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
- 5.登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

5.5.2 代码开发实现
第一步:创建项目,导入html页面,配置文件,jar包
- 使用IDEA创建JavaWeb项目

- 导入jar包
在WEB-INF目录下创建lib文件夹,然后将jar包放入


- 将index.jsp改造成一个登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>案例:用户登录</title>
</head>
<body>
<form action="/demo/loginServlet" method="post">
用户名:<input type="text" name="username"> <br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
- 在src目录创建jdbc.properties文件配置数据库连接信息
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=root
# 由于使用了连接池,所以需要注意上面的键名,不能随便取
第二步:创建数据库环境
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`password` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
第三步:创建包com.demo.domain,创建实体类User.java
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
第四步:创建com.demo.util包,编写工具类JDBCUtils.java
public class JDBCUtils {
private static DataSource ds ;
static {
try {
//1.加载配置文件
Properties pro = new Properties();
//使用ClassLoader加载配置文件,获取字节输入流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
pro.load(is);
//2.初始化连接池对象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接池对象
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 获取连接Connection对象
*/
public static Connection getConnection() throws SQLException, SQLException {
return ds.getConnection();
}
}
第五步:创建包com.demo.dao,创建类UserDao.java,提供login()方法
public class UserDao {
//声明JDBCTemplate对象共用
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
/**
* 登录方法
*
* @param loginUser 只有用户名和密码
* @return user包含用户全部数据, 没有查询到,返回null
*/
public User login(User loginUser) {
//1.编写sql
String sql = "select * from user where username = ? and password = ?";
//2.调用query方法
User user = null;
try {
} catch (Exception e) {
e.printStackTrace();
return null;
}
return user;
}
}
第六步:编写com.demo.web.servlet.LoginServlet类
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置编码
req.setCharacterEncoding("utf-8");
//2.获取请求参数
String username = req.getParameter("username");
String password = req.getParameter("password");
//3.封装user对象
User loginUser = new User();
loginUser.setUsername(username);
loginUser.setPassword(password);
//4.调用UserDao的login方法
UserDao dao = new UserDao();
User user = dao.login(loginUser);
//5.判断user
if (user == null) {
//登录失败
req.getRequestDispatcher("/failServlet").forward(req, resp);
} else {
//登录成功
//存储数据
req.setAttribute("user", user);
//转发
req.getRequestDispatcher("/successServlet").forward(req, resp);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
第七步:编写FailServlet.java和SuccessServlet.java类
SuccessServlet.java
@WebServlet("/successServlet")
public class SuccessServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
//获取request域中共享的user对象
User user = (User) request.getAttribute("user");
if (user != null) {
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出
response.getWriter().write("登录成功!" + user.getUsername() + ",欢迎您");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
this.doPost(request, response);
}
}
FailServlet.java
@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置编码
response.setContentType("text/html;charset=utf-8");
//输出
response.getWriter().write("登录失败,用户名或密码错误");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
然后运行tomcat测试效果。
5.5.3 资源路径写法
- login.html中form表单的action路径的写法:虚拟目录+Servlet的资源路径。

6. Servlet的Response对象

6.1 Response对象的功能
功能:设置响应消息(响应行、响应头、响应体)。
6.1.1 设置响应行
响应行格式:
HTTP/1.1 200 ok
- 设置状态码的方法:setStatus(int sc)
6.1.2 设置响应头
- 设置响应头:setHeader(String name, String value)
6.1.3 设置响应体
即将数据输出到客户端通过响应体。使用步骤:
- 1. 获取输出流
- 字符输出流:PrintWriter getWriter()
- 字节输出流:ServletOutputStream getOutputStream()
- 2. 使用输出流,将数据输出到客户端浏览器
6.2 Response对象的功能的应用
6.2.1 重定向
(1)重定向:即资源跳转的方式。

(2)重定向的代码实现。如下:
// 1. 设置状态码为302
response.setStatus(302);
// 2.设置响应头location
response.setHeader("location","/demo/demoServlet");
或者用下面更简单的方式
//简单的重定向方法
response.sendRedirect("/demo/demoServlet");
(代码实例参考:DemoServlet1_1.java与DemoServlet1_2.java)
(3)重定向的特点与转发的特点
- 重定向的特点(redirect)
- 1. 地址栏发生变化
- 2. 重定向可以访问其他站点(服务器)的资源。既可以重定向到如百度这样的网址去。
- 3. 重定向是两次请求。不能使用request对象来共享数据
- 转发的特点(forward)
- 1. 转发地址栏路径不变
- 2. 转发只能访问当前服务器下的资源
- 3. 转发是一次请求,可以使用request对象来共享数据
(4)重定向的路径写法
路径分为相对路径和绝对路径。下面说下两种路径的写法:
- 1. 相对路径:通过相对路径不可以确定唯一资源
- 例如:./index.html
- 特点:不以/开头,以.开头路径
- 规则:找到当前资源和目标资源之间的相对位置关系
- ./:表示当前目录(./可以省略不写)
- ../:表示后退一级目录
- 2. 绝对路径:通过绝对路径可以确定唯一资源
- 例如:http://localhost:8080/demo/demoServlet 或 /demo/demoServlet
- 特点:以/开头的路径
- 规则:判断定义的路径是给谁用的?判断请求将来从哪儿发出
- 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
- 建议虚拟目录动态获取:request.getContextPath(),避免虚拟目录变化。例如<a> , <form>,重定向...
- 给服务器使用:不需要加虚拟目录
- 转发路径。如request.getRequestDispather("/demoServlet").forward(request,response)
- 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
(代码实例参考:location.jsp、location2.jsp)
6.2.2 服务器输出字符串数据到浏览器
(1)实现步骤:
- 1. 获取字符输出流
- 2. 输出数据
(2)需要注意中文乱码问题
不同的浏览器解码不一样,所以可能出现乱码问题。
从IE浏览器查看编码,使用的GBK解码,浏览器默认按照什么编码解码跟电脑系统环境有关信息。

故解决步骤如下:
- 1. PrintWriter pw = response.getWriter();获取的流的默认编码是ISO-8859-1
- 2. 设置该流的默认编码
- 3. 告诉浏览器响应体使用的编码
//获取流对象之前,设置流的默认编码:ISO-8859-1 设置为:GBK
response.setCharacterEncoding("utf-8");
//告诉浏览器,服务器发送的消息体数据的编码。建议浏览器使用该编码解码
response.setHeader("content-type","text/html;charset=utf-8");
更简单的形式:
//简单的形式,设置编码,是在获取流之前设置
response.setContentType("text/html;charset=utf-8");
代码实例如下:
@WebServlet("/demo2")
public class DemoServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// //获取流对象之前,设置流的默认编码:ISO-8859-1 设置为:GBK
// response.setCharacterEncoding("utf-8");
// //告诉浏览器,服务器发送的消息体数据的编码。建议浏览器使用该编码解码
// response.setHeader("content-type","text/html;charset=utf-8");
//简单的形式,设置编码,是在获取流之前设置
response.setContentType("text/html;charset=utf-8");
//1.获取字符输出流
PrintWriter pw = response.getWriter();
//2.输出数据
pw.write("<h1>hello response</h1>");
pw.write("您好!response");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
6.2.3 服务器输出字节数据到浏览器
实现步骤:
- 1. 获取字节输出流
- 2. 输出数据
代码实例如下:
@WebServlet("/demo3")
public class DemoServlet3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
//1.获取字节输出流
ServletOutputStream sos = response.getOutputStream();
//2.输出数据
sos.write("你好".getBytes("utf-8"));
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
6.2.4 实现验证码
验证码的本值就是一张图片,而输入的是图片中的信息。之所以使用验证码是为了防止有人恶意注册,用机器不断自动填表单注册。
code.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<img id="checkCode" src="<%=request.getContextPath()%>/codeServlet"/>
<a id="change" href="">看不清换一张?</a>
</body>
<script>
/*
分析:
点击超链接或者图片,需要换一张
1.给超链接和图片绑定单击事件
2.重新设置图片的src属性值
*/
window.onload = function () {
//1.获取图片对象
var img = document.getElementById("checkCode");
//2.绑定单击事件
img.onclick = function () {
//加时间戳
var date = new Date().getTime();
img.src = "<%=request.getContextPath()%>/codeServlet?" + date;
}
}
</script>
</html>
CodeServlet.java
@WebServlet("/codeServlet")
public class CodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int width = 100;
int height = 50;
//1.创建一对象,在内存中图片(验证码图片对象)
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//2.美化图片
//2.1 填充背景色
Graphics g = image.getGraphics();//画笔对象
g.setColor(Color.PINK);//设置画笔颜色
g.fillRect(0,0,width,height);
//2.2画边框
g.setColor(Color.BLUE);
g.drawRect(0,0,width - 1,height - 1);
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789";
//生成随机角标
Random ran = new Random();
for (int i = 1; i <= 4; i++) {
int index = ran.nextInt(str.length());
//获取字符
char ch = str.charAt(index);//随机字符
//2.3写验证码
g.drawString(ch+"",width/5*i,height/2);
}
//2.4画干扰线
g.setColor(Color.GREEN);
//随机生成坐标点
for (int i = 0; i < 10; i++) {
int x1 = ran.nextInt(width);
int x2 = ran.nextInt(width);
int y1 = ran.nextInt(height);
int y2 = ran.nextInt(height);
g.drawLine(x1,y1,x2,y2);
}
//3.将图片输出到页面展示
ImageIO.write(image,"jpg",response.getOutputStream());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
说下在请求路径后面加时间参数的问题,如果不加任何参数,把src值设为"/demo/codeServlet",那么图片资源只能被更新一次,因为就会被浏览器缓存下来,再刷新也不会显示新的验证码,之所以时间作为参数,是因为时间是不断向前的,不会出现被一直缓存的情况,即使是随机数也可能出现某两次相同的情况,而不能刷新得到新的验证码。

6.3 ServletContext对象
6.3.1 概念
代表整个web应用,可以和程序的容器(服务器)来通信。
6.3.2 获取该对象的方法
- 1. 通过request对象获取:request.getServletContext();
- 2. 通过HttpServlet获取:this.getServletContext();
代码实例:
@WebServlet("/demo4")
public class DemoServlet4 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取ServletContext对象的方式
ServletContext servletContext1 = request.getServletContext();
ServletContext servletContext2 = this.getServletContext();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
6.3.3 功能
- 1. 获取MIME类型:
- MIME类型:在互联网通信过程中定义的一种文件数据类型
- 格式: 大类型/小类型 如:text/html、image/jpeg
- 获取:String getMimeType(String file) 。在tomcat下的conf下的web.xml中有这些对应的映射。
- MIME类型:在互联网通信过程中定义的一种文件数据类型
- 2. 域对象:共享数据(ServletContext对象范围:所有用户所有请求的数据)
- 1. setAttribute(String name,Object value)
- 2. getAttribute(String name)
- 3. removeAttribute(String name)
- 3. 获取文件的真实(服务器)路径
- 访问web目录下的资源
- String b = context.getRealPath("/b.txt");
- 访问WEB-INF目录下的资源
- String c = context.getRealPath("/WEB-INF/c.txt");
- 访问src目录下的资源
- String a = context.getRealPath("/WEB-INF/classes/a.txt");
- 访问web目录下的资源
获取MIME类型的代码实例:
DemoServelt5.java
@WebServlet("/demo5")
public class DemoServlet5 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = request.getServletContext();
String mimeType = servletContext.getMimeType("a.html");
System.out.println(mimeType);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
可以在自己配置的tomcat下的conf下的web.xml查看


域对象共享数据的代码实例:
DemoServlet6_1.java
@WebServlet("/demo6_1")
public class DemoServlet6_1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = request.getServletContext();
servletContext.setAttribute("msg","Hello ServletContext");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
DemoServlet6_2.java
@WebServlet("/demo6_2")
public class DemoServlet6_2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取ServletContext对象
ServletContext servletContext = request.getServletContext();
// 通过getAttribute(name)方法获取存储的值
Object msg = servletContext.getAttribute("msg");
System.out.println(msg);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
获取文件真实路径的代码实例:
a.txt、b.txt、c.txt文件所处位置:

@WebServlet("/demo7")
public class DemoServlet7 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ServletContext对象
ServletContext servletContext = request.getServletContext();
// 1.访问web目录下的资源
String realPath1 = servletContext.getRealPath("/b.txt");
System.out.println(realPath1);
// 2.访问WEB-INF目录下的资源
String realPath2 = servletContext.getRealPath("/WEB-INF/c.txt");
System.out.println(realPath2);
// 3.访问src目录下的资源
String realPath3 = servletContext.getRealPath("/WEB-INF/classes/a.txt");
System.out.println(realPath3);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
6.4 案例之文件下载
6.4.1 文件下载需求
- 1. 页面显示超链接
- 2. 点击超链接后弹出下载提示框
- 3. 完成图片文件下载
6.4.2 分析
1. 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
2. 任何资源都必须弹出下载提示框
3. 使用响应头设置资源的打开方式:
* content-disposition:attachment;filename=xxx
6.4.3 代码实现
步骤:
1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
2. 定义Servlet
1. 获取文件名称
2. 使用字节输入流加载文件进内存
3. 指定response的响应头: content-disposition:attachment;filename=xxx
4. 将数据写出到response输出流
问题:
中文文件问题
解决思路:
1. 获取客户端使用的浏览器版本信息
2. 根据不同的版本信息,设置filename的编码方式不同


index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件下载</title>
</head>
<body>
<img src="/img/kyz.jpg" alt="鷇音子" width="500" height="400">
<a href="/downloadServlet?fileName=kyz.jpg">下载</a>
<br>
<img src="/img/素还真.jpg" alt="素还真" width="500" height="400">
<a href="/downloadServlet?fileName=素还真.jpg">下载</a>
</body>
</html>
DownloadServlet.java
@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
// 1. 获取文件名称
String fileName = request.getParameter("fileName");
// 2. 使用字节输入流加载文件进内存
// 2.1 获取文件在服务器端的路径
String path = request.getServletContext().getRealPath("/img/" + fileName);
FileInputStream fis=new FileInputStream(new File(path));
// 3.设置response的响应头
// 3.1设置响应头类型:content-type
response.setHeader("content-type",request.getServletContext().getMimeType(fileName));
//解决中文文件名问题
// 1.获取user-agent请求头、
String agent = request.getHeader("user-agent");// 能够判断是什么浏览器打开的
// 2.使用工具类方法编码文件名即可
fileName = getFileName(agent, fileName);
// 3.2设置响应头打开方式:content-disposition
response.setHeader("content-disposition","attachment;filename="+fileName);
// 4.将输入流的数据写出到输出流中
ServletOutputStream sos = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while((len = fis.read(buff)) != -1){
sos.write(buff,0,len);
}
fis.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
public static String getFileName(String agent, String filename) throws UnsupportedEncodingException, UnsupportedEncodingException {
// 根据不同的user-agent来判断是什么浏览器,来对应处理编码问题
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}
7. 会话技术
7.1 概述
- 1. 会话:一次会话中包含多次请求和响应。
- 一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止
- 2. 功能:在一次会话的范围内的多次请求间,共享数据
- 3. 方式:
- 1. 客户端会话技术:Cookie
- 2. 服务器端会话技术:Session
7.2 Cookie
7.2.1 概念
Cookie:客户端会话技术,将数据保存到客户端。
7.2.2 快速入门
使用步骤:
- 1. 创建Cookie对象,绑定数据:new Cookie(String name, String value)
- 2. 发送Cookie对象:response.addCookie(Cookie cookie)
- 3. 获取Cookie,拿到数据:Cookie[] request.getCookies()
7.2.3 实现原理
- (1)客户端浏览器向服务器发送请求,如果有需要,服务器就将数据保存到cookie并通过响应头随着响应客户端传到客户端浏览器保存。
- (2)此时客户端浏览器就已经有了cookie,又是一次新的请求,应该说以后每一次新的请求就将cookie放到请求头中随着请求传输到服务器,服务端就可以读取到该cookie。

我们创建CookieServlet1.java和CookieServlet2.java
@WebServlet("/cookie1")
public class CookieServlet1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 创建Cookie对象
Cookie cookie=new Cookie("msg","hello");
// 2. 发送Cookie对象
response.addCookie(cookie);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
@WebServlet("/cookie2")
public class CookieServlet2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 3. 获取Cookie
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
System.out.println(name+"="+value);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
输入http://localhost:8080/cookie1后发现响应头有了set-cookie:msg=hello。即表示当服务器在接收到请求后根据代码把cookie又响应到浏览器端了。

输入http://localhost:8080/cookie2后发现请求头中带的cookie中有"msg=hello"。

同时控制台还有结果打印:

7.2.4 cookie细节
1. 一次可不可以发送多个cookie?
可以。可以创建多个Cookie对象,使用response调用多次addCookie方法发送cookie即可。
代码实例:
@WebServlet("/cookie3")
public class CookieServlet3 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie cookie1=new Cookie("username","zhangsan");
Cookie cookie2=new Cookie("password","123456");
response.addCookie(cookie1);
response.addCookie(cookie2);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}

2. cookie在浏览器中保存多长时间?
(1)默认情况下,当浏览器关闭后,Cookie数据被销毁。
(2)持久化存储:setMaxAge(int seconds),参数表示秒数
①. 正数:将Cookie数据写到硬盘的文件中。持久化存储。并指定cookie存活时间,时间到后,cookie文件自动失效
②. 负数:默认值
③. 零:删除cookie信息
@WebServlet("/cookie4")
public class CookieServlet4 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie cookie =new Cookie("tempInfo","save time....");
cookie.setMaxAge(20);// 存活20秒
response.addCookie(cookie);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
3. cookie能不能存中文?
- 在tomcat 8 之前 cookie中不能直接存储中文数据。需要将中文数据转码---一般采用URL编码(%E3)
- 在tomcat 8 之后,cookie支持中文数据。特殊字符还是不支持,建议使用URL编码存储,URL解码解析
CookieServlet5_1.java
@WebServlet("/cookie5_1")
public class CookieServlet5_1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// URLEncoder.encode()编码
String encodeString = URLEncoder.encode("中文信息","utf-8");
Cookie cookie=new Cookie("chinese",encodeString);
response.addCookie(cookie);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
CookieServlet5_2.java
@WebServlet("/cookie5_2")
public class CookieServlet5_2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if ("chinese".equals(cookie.getName())) {
String value = cookie.getValue();
// 解码前打印
System.out.println(value);
// URLDecoder.decode()解码
value = URLDecoder.decode(value,"gbk");
System.out.println(value);
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}

4. cookie共享问题?
1. 假设在一个tomcat服务器中,部署了多个web项目,那么在这些web项目中cookie能不能共享?
- 默认情况下cookie不能共享
- setPath(String path):设置cookie的获取范围。默认情况下,设置当前的虚拟目录。如果要共享,则可以将path设置为"/"
2. 不同的tomcat服务器间cookie共享问题?
- setDomain(String path):如果设置一级域名相同,那么多个服务器之间cookie可以共享。例如:setDomain(".baidu.com"),那么tieba.baidu.com和news.baidu.com中cookie可以共享
7.2.5 Cookie的特点和作用
cookie的特点:
- 1. cookie存储数据在客户端浏览器
- 2. 浏览器对于单个cookie 的大小有限制(4kb) 以及 对同一个域名下的总cookie数量也有限制(20个)
cookie的作用:
- 1. cookie一般用于存出少量的不太敏感的数据(因为cookie是保存在客户端浏览器的,不太安全)
- 2. 在不登录的情况下,完成服务器对客户端的身份识别(例如实现‘记住我’功能)
7.2.6 案例-记住上一次访问时间
1. 需求:
1. 访问一个Servlet,如果是第一次访问,则提示:您好,欢迎您首次访问。
2. 如果不是第一次访问,则提示:欢迎回来,您上次访问时间为:显示时间字符串
2. 分析:
1. 可以采用Cookie来完成
2. 在服务器中的Servlet判断是否有一个名为lastTime的cookie
1. 有:不是第一次访问
1. 响应数据:欢迎回来,您上次访问时间为:2018年6月10日11:50:20
2. 写回Cookie:lastTime=2018年6月10日11:50:01
2. 没有:是第一次访问
1. 响应数据:您好,欢迎您首次访问
2. 写回Cookie:lastTime=2018年6月10日11:50:01
3. 代码实例(CookieDemoServlet.java):
@WebServlet("/cookieDemo")
public class CookieDemoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置响应的消息体的数据格式以及编码
response.setContentType("text/html;charset=utf-8");
//1.获取所有Cookie
Cookie[] cookies = request.getCookies();
boolean flag = false;//没有cookie为lastTime
//2.遍历cookie数组
if (cookies != null && cookies.length > 0) {
for (Cookie cookie : cookies) {
//3.获取cookie的名称
String name = cookie.getName();
//4.判断名称是否是:lastTime
if ("lastTime".equals(name)) {
//有该Cookie,不是第一次访问
flag = true;//有lastTime的cookie
//设置Cookie的value
//获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = sdf.format(date);
System.out.println("编码前:" + str_date);
//URL编码
str_date = URLEncoder.encode(str_date, "utf-8");
System.out.println("编码后:" + str_date);
cookie.setValue(str_date);
//设置cookie的存活时间
cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
response.addCookie(cookie);
//响应数据
//获取Cookie的value,时间
String value = cookie.getValue();
System.out.println("解码前:" + value);
//URL解码:
value = URLDecoder.decode(value, "utf-8");
System.out.println("解码后:" + value);
response.getWriter().write("<h1>欢迎回来,您上次访问时间为:" + value + "</h1>");
break;
}
}
}
if (cookies == null || cookies.length == 0 || flag == false) {
//没有,第一次访问
//设置Cookie的value
//获取当前时间的字符串,重新设置Cookie的值,重新发送cookie
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String str_date = sdf.format(date);
System.out.println("编码前:" + str_date);
//URL编码
str_date = URLEncoder.encode(str_date, "utf-8");
System.out.println("编码后:" + str_date);
Cookie cookie = new Cookie("lastTime", str_date);
//设置cookie的存活时间
cookie.setMaxAge(60 * 60 * 24 * 30);//一个月
response.addCookie(cookie);
response.getWriter().write("<h1>您好,欢迎您首次访问</h1>");
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
7.3 Session
7.3.1 概念
session是服务器端会话技术,在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中。
7.3.2 快速入门
可以通过HttpSession对象来共享数据。
- 1. 获取HttpSession对象:
- HttpSession session = request.getSession();
- 2. 使用HttpSession对象:
- Object getAttribute(String name) :获取名为name的session数据
- void setAttribute(String name, Object value):设置session数据
- void removeAttribute(String name) :清除指定session
7.3.3 原理
Session的实现是依赖于Cookie。

下面用两个Servlet.java类演示:
SessionDemo1.java
@WebServlet("/session1")
public class SessionDemo1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取session
HttpSession session = request.getSession();
System.out.println("SessionDemo1 id: "+session.getId());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
SessionDemo2.java
@WebServlet("/session2")
public class SessionDemo2 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取session
HttpSession session = request.getSession();
System.out.println("SessionDemo2 id: " + session.getId());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
然后运行tomcat,输入http://localhost:8080/session1
可以在浏览器的抓包工具面板和IDEA的控制台看到如下信息:

即服务端在内存中创建了一个Session对象,该对象有一个ID,然后在响应客户端浏览器时作为响应头传输过去。
接着在浏览器地址栏输入:http://localhost:8080/session2
观察浏览器抓包工具该请求的请求头与IDEA控制台打印结果

可以发现在发送这一次请求时将保存在cookie中的JSESSIONID作为请求头数据又传输到服务器端了,所以SessionDemo2.java打印的id是同一个,因此SessionDemo1.java和SessionDemo2.java中的session对象是同一个,就可以共享数据。
7.3.4 细节
1. 当客户端关闭后,服务器不关闭,两次获取session是否为同一个?
- 默认情况下。不是。(为什么?因为默认情况下,当浏览器关闭后,Cookie数据被销毁,而session实现是基于cookie的。)
- 如果需要相同,则可以创建Cookie,键为JSESSIONID,设置最大存活时间,让cookie持久化保存。
Cookie c = new Cookie("JSESSIONID",session.getId());
c.setMaxAge(60*60);
response.addCookie(c);
代码实例演示:
还是上面两个代码,这次先输入http://localhost:8080/session1然后关闭浏览器,关闭后再打开浏览器,输入http://localhost:8080/session2查看控制台打印结果

发现两次获取的session不是同一个了。
将SessionDemo1.java改为如下:
@WebServlet("/session1")
public class SessionDemo1 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取session
HttpSession session = request.getSession();
Cookie cookie=new Cookie("JSESSIONID",session.getId());
cookie.setMaxAge(60*60);// 保存一小时
response.addCookie(cookie);
System.out.println("SessionDemo1 id: "+session.getId());
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
再次重复上面的试验,这次发现IDEA控制台打印结果一致了。

2. 客户端不关闭,服务器关闭后,两次获取的session是同一个吗?
- 不是同一个,但是要确保数据不丢失。tomcat自动完成以下工作,但IDEA无法完成,因为idea会自动删除work目录
- session的钝化:在服务器正常关闭之前,将session对象系列化到硬盘上
- session的活化:在服务器启动后,将session文件转化为内存中的session对象即可。
代码实例:
还是上面的两段代码,运行输入http://localhost:8080/session1控制台打印如下:

复制下面的目录,等会会用到,这是idea产生的目录,并不是本地tomcat的部署目录。

去到刚才复制的目录下,进入到work目录下

里面的目录并没有任何文件,不过有空文件夹,因为session的钝化发生在服务器正常关闭之前,将session对象系列化到硬盘上,因此将服务器正常关闭。

然后再进入到ROOT目录下,会发现生成的SESSION文件

用记事本打开,尽管都是乱码,不过还是发现了保存下来的session的id

当再次启动项目时会自动删除work目录,重新生成

打开新生成的work目录就是一个空的,没有保存下来的session文件了,所以获取的不是同一个session了。

但如果是本地的tomcat则不会删除,会自动完成。
将IDEA生成的下面这个目录复制到tomcat的webapps下,并重命名demo13


去bin目录下启动tomcat,然后在浏览器输入http://localhost:8080/demo13/session1,黑框会输出如下:
![]()
在未正常关闭tomcat前,work目录内是没有文件的,但有一些文件夹

现在正常关闭tomcat

再去work目录,demo13就有生成的session文件了

打开查看,和上面控制台输出的一致

现在又重启tomcat,输入http://localhost:8080/demo13/session2,查看控制台打印
![]()
两次输入是同一个session对象,虽然work目录没有了这个文件,因为再次被读取了。
3. session什么时候被销毁?
- 1. 服务器关闭
- 2. session对象调用invalidate() 。
- 3. session默认失效时间 30分钟,可以选择修改配置文件web.xml文件
<session-config>
<session-timeout>30</session-timeout>
</session-config>
7.3.5 session特点
- 1. session用于存储一次会话的多次请求的数据,存在服务器端
- 2. session可以存储任意类型,任意大小的数据
session与Cookie的区别:
- 1. session存储数据在服务器端,Cookie在客户端
- 2. session没有数据大小限制,Cookie有
- 3. session数据安全,Cookie相对于不安全
本文介绍了Web开发的基本概念,包括软件架构、资源分类、网络通信三要素等,并深入探讨了Tomcat服务器的安装、配置与使用方法。此外,还讲解了Servlet的基础知识、生命周期及其配置方式,以及HTTP协议的工作原理。
1963

被折叠的 条评论
为什么被折叠?



