一、HTTP协议
1、HTTP协议概念
1.1 什么是HTTP协议
HTTP:Hyper Text Transfer Protocol超文本传输协议。
1.2 HTTP协议的作用
HTTP最大的作用就是确定了请求和响应数据的格式。浏览器发送给服务器的数据:请求报文;服务器返回给浏览器的数据:响应报文。
1.3 HTTP协议的组成
HTTP协议由请求和响应两部分构成,请求是由客户端往服务器传输数据,响应是由服务器往客户端传输数据。
2、请求报文
2.1 在开发者工具中浏览报文源码
2.2 请求报文的三个部分
2.2.1 请求行
作用: 展示当前请求的最基本信息
POST /dynamic/target.jsp HTTP/1.1
- 请求方式
- 访问地址
- HTTP协议的版本
2.2.2 请求消息头
作用: 通过具体的参数对本次请求进行详细的说明
格式: 键值对,键和值之间使用冒号隔开
相对比较重要的请求消息头:
名称 | 功能 |
---|---|
Host | 服务器的主机地址 |
Accept | 声明当前请求能够接受的『媒体类型』 |
Referer | 当前请求来源页面的地址 |
Content-Length | 请求体内容的长度 |
Content-Type | 请求体的内容类型,这一项的具体值是媒体类型中的某一种 |
Cookie | 浏览器访问服务器时携带的Cookie数据 |
2.2.3 请求体
作用:作为请求的主体,发送数据给服务器。具体来说其实就是POST请求方式下的请求参数。
格式:
1. form data
含义:当前请求体是一个表单提交的请求参数。
查看源码后,发现格式如下:
username=tom&password=123456
- 每一组请求参数是一个键值对
- 键和值中间是等号
- 键值对之间是&号
2.Request Payload
含义:整个请求体以某种特定格式来组织数据,例如JSON格式。
3、请求方式
3.1 HTTP协议已定义的请求方式
HTTP1.1中共定义了八种请求方式:
- GET:从服务器端获取数据
- POST:将数据保存到服务器端
- PUT:命令服务器对数据执行更新
- DELETE:命令服务器删除数据
- HEAD
- CONNECT
- OPTIONS
- TRACE
3.2 GET请求
- 特征1:没有请求体
- 特征2:请求参数附着在URL地址后面
- 特征3:请求参数在浏览器地址栏能够直接被看到,存在安全隐患
- 特征4:在URL地址后面携带请求参数,数据容量非常有限。如果数据量大,那么超出容量的数据会丢失
- 特征5:从报文角度分析,请求参数是在请求行中携带的,因为访问地址在请求行
3.3 POST请求
- 特征1:有请求体
- 特征2:请求参数放在请求体中
- 特征3:请求体发送数据的大小没有限制
- 特征4:可以发送各种不同类型的数据
- 特征5:从报文角度分析,请求参数是在请求体中携带的
- 特征6:由于请求参数是放在请求体中,所以浏览器地址栏看不到
4、媒体类型
4.1 HTTP协议中的MIME类型
Multipurpose Internet Mail Extensions
4.2 用途
为了让用户通过浏览器和服务器端交互的过程中有更好、更丰富的体验,HTTP协议需要支持丰富的数据类型。
4.3 MIME类型定义参考
我们可以通过查看Tomcat解压目录下conf/web.xml配置文件,了解HTTP协议中定义的MIME类型。
<mime-mapping>
<extension>mp4</extension>
<mime-type>video/mp4</mime-type>
</mime-mapping>
<mime-mapping>
<extension>doc</extension>
<mime-type>application/msword</mime-type>
</mime-mapping>
<mime-mapping>
<extension>json</extension>
<mime-type>application/json</mime-type>
</mime-mapping>
<mime-mapping>
<extension>html</extension>
<mime-type>text/html</mime-type>
</mime-mapping>
从上面的例子中可以看出:MIME的基本格式是
大类/具体类型
MIME类型在HTTP报文中对应的是内容类型:Content-type
5、响应报文
5.1 响应状态行
HTTP/1.1 200 OK
- HTTP协议版本
- 响应状态码
- 响应状态的说明文字
5.2 响应消息头
- 响应体的说明书。
- 服务器端对浏览器端设置数据,例如:服务器端返回Cookie信息。
名称 | 功能 |
---|---|
Content-Type | 响应体的内容类型 |
Content-Length | 响应体的内容长度 |
Set-Cookie | 服务器返回新的Cookie信息给浏览器 |
location | 在重定向的情况下,告诉浏览器访问下一个资源的地址 |
5.3 响应体
服务器返回的数据主体,有可能是各种数据类型。
- HTML页面
- 图片
- 视频
- 以下载形式返回的文件
- CSS文件
- JavaScript文件
5.4 响应状态码
作用:以编码的形式告诉浏览器当前请求处理的结果
状态码 | 含义 |
---|---|
200 | 服务器成功处理了当前请求,成功返回响应 |
302 | 重定向 |
400 | [SpringMVC特定环境]请求参数问题 |
403 | 没有权限 |
404 | 找不到目标资源 |
405 | 请求方式和服务器端对应的处理方式不一致 |
406 | [SpringMVC特定环境]请求扩展名和实际返回的响应体类型不一致 |
50X | 服务器端内部错误,通常都是服务器端抛异常了 |
404产生的具体原因:
- 访问地址写错了,确实是没有这个资源
- 访问了WEB-INF目录下的资源
- Web应用启动的时候,控制台已经抛出异常,导致整个Web应用不可用,访问任何资源都是404
- 服务器端缓存
二、Servlet入门
1、Web资源的概念
Web资源就是运行在服务器上的资源,它一共分为两类: 静态资源以及动态资源
1.1 静态资源
静态资源就是web页面中供人们浏览的数据始终是不变的,例如html、css、js、图片、音视频等等都属于静态资源
1.2 动态资源
动态资源就是web页面中供人们浏览的数据是由程序产生的,不同的用户或者不同时间点访问web页面看到的内容各不相同,例如后续学习的Servlet、JSP(不学)、Thymeleaf等等都是动态资源
2、Servlet的概念
2.1 什么是Servlet
Servlet 是运行在服务端(tomcat)的Java小程序,是sun公司提供一套定义动态资源规范; 从代码层面上来讲Servlet就是一个接口
2.2 Servlet的作用
用来接收、处理客户端请求、响应给浏览器的动态资源。在整个Web应用中,Servlet主要负责处理请求、协调调度功能。我们可以把Servlet称为Web应用中的控制器
2.2 Servlet的作用的图示
要实现的效果
使用Servlet实现的具体细节
3、Servlet的入门案例
3.1 目标
在页面上点击超链接,由Servlet处理这个请求,并返回一个响应字符串:Hello,I am Servlet
3.2 思路
3.3 实现步骤
第一步: 创建动态Web module
第二步:创建html页面
<!-- /Web应用地址/Servlet地址 -->
<a href="/app/helloServlet">Servlet Hello World</a>
第三步:创建HelloServlet类
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 {
// 控制台打印,证明这个方法被调用了
System.out.println("我是HelloServlet,我执行了!");
// 返回响应字符串
// 1、获取能够返回响应数据的字符流对象
PrintWriter writer = servletResponse.getWriter();
// 2、向字符流对象写入数据
writer.write("Hello,I am Servlet");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
第四步:在web.xml中配置HelloServlet
配置文件位置:WEB-INF/web.xml
<!-- 配置Servlet本身 -->
<servlet>
<!-- 全类名太长,给Servlet设置一个简短名称 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 配置Servlet的全类名 -->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
</servlet>
<!-- 将Servlet和访问地址关联起来 -->
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/helloServlet</url-pattern>
</servlet-mapping>
映射路径:Servlet并不是文件系统中实际存在的目录或文件,所以为了方便浏览器访问,我们创建了映射路径来访问它。
3.4 小结
需求:在浏览器上点超链接能够访问Java程序
4、概念梳理
4.1 原生Tomcat
安装在电脑上的实实在在的Tomcat软件
4.2 IDEA中的Tomcat实例
通过idea的配置在idea上集成的Tomcat实例,其实还是使用的原生的Tomcat软件
4.3 IDEA中的Web工程
我们使用IDEA编写的动态Web工程,该工程只是用于进行编码,实际上部署运行在Tomcat服务器中的并不是这个工程
4.4 根据Web工程生成的war包
根据我们创建的动态Web工程,IDEA会将其打包成一个war包,而真正部署运行在Tomcat服务器中的其实是war包
4.5 访问资源的地址
4.5.1 访问静态资源
/Web应用名称/静态资源本身的路径
4.5.2 访问动态资源
/Web应用名称/映射路径
4.6 Web应用名称
注意Web应用名不是你工程或者Module的名字,而是你在部署时候的ApplicationContext的内容
4.7 总体的逻辑结构
5、Servlet的注解方式配置(了解即可)
注意: 一个Servlet要么使用配置文件方式配置,要么使用注解方式配置,不能两者都使用
使用注解方式配置的优势: 代码更加简单
使用注解方式配置的劣势: 耦合度高
一般更推荐使用配置文件方式配置Servlet,尤其是配置第三方框架中的Servlet:例如SpringMVC中的DispatcherServlet,我们只能够使用配置文件方式进行配置
5.1 使用注解方式配置的代码实现
在要进行配置的Servlet类上添加注解@WebServlet("/映射路径")
三、Servlet进阶
1、Servlet的生命周期和生命周期方法
1.1 什么是Servlet的生命周期
Servlet的生命周期就是servlet从创建到销毁的过程,我们所要去探讨的就是Servlet对象在什么时候创建出来以及在什么时候销毁。当然创建和销毁Servlet对象的工作是不需要我们去做的
1.2 Servlet对象什么时候创建
默认情况下是在第一次有请求访问该Servlet实例的时候就会创建该Servlet对象
1.3 Servlet对象什么时候销毁
在服务器关闭,或者当前项目从服务器中移除的时候会销毁当前项目中的所有Servlet对象
2、Servlet的生命周期方法
2.1 什么是Servlet的生命周期方法
在Servlet的生命周期中必然会经历的方法我们称之为Servlet的生命周期方法,总共包含三个方法:init、service、destroy
2.2 init方法
该方法会在Servlet实例对象被创建出来之后执行,我们可以在该方法中获取当前Servlet的初始化参数,以及进行一些读取配置文件之类的操作
2.3 service方法
该方法会在Servlet实例对象每次接收到请求的时候均执行,我们可以在该方法中接收、处理请求,以及将客户端需要的数据响应给客户端
2.4 destroy方法
该方法会在Servlet实例对象销毁之前执行,我们可以在该方法中做一些资源回收、释放、关闭等等操作
3、配置Servlet提前创建
有时候我们需要在Servlet创建的时候做一些资源加载等等耗时操作,所以如果Servlet在第一次接收请求的时候才创建的话必然会影响用户的访问速度,所以此时我们需要让Servlet提前创建,将Servlet的创建提前到服务器启动的时候。
通过修改web.xml中Servlet的配置可以实现:
<!-- 配置Servlet本身 -->
<servlet>
<!-- 全类名太长,给Servlet设置一个简短名称 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 配置Servlet的全类名 -->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
<!-- 配置Servlet启动顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
4、ServletConfig的介绍
4.1 接口概览
4.2 接口方法介绍
方法名 | 作用 |
---|---|
getServletName() | 获取<servlet-name>HelloServlet</servlet-name>定义的Servlet名称 |
getServletContext() | 获取ServletContext对象 |
getInitParameter() | 获取配置Servlet时设置的『初始化参数』,根据名字获取值 |
getInitParameterNames() | 获取所有初始化参数名组成的Enumeration对象 |
4.3 获取Servlet的初始化参数
我们可以在web.xml中对Servlet配置初始化参数,接下来可以在Servlet的init方法中获取配置的初始化参数的值
web.xml代码
<!-- 配置Servlet本身 -->
<servlet>
<!-- 全类名太长,给Servlet设置一个简短名称 -->
<servlet-name>HelloServlet</servlet-name>
<!-- 配置Servlet的全类名 -->
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
<!-- 配置初始化参数 -->
<init-param>
<param-name>goodMan</param-name>
<param-value>me</param-value>
</init-param>
<!-- 配置Servlet启动顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
5、Servlet的体系结构(了解)
5.1 类型关系
Servlet接口有一个实现类是GenericServlet,而GenericServlet有一个子类是HttpServlet,我们创建Servlet的时候会选择继承HttpServlet,因为它里面相当于也实现了Servlet接口,并且对一些方法做了默认实现;而且子类的功能会比父类的更加强大
5.2 方法关系
我们编写Servlet类继承HttpServlet的时候,只需要重写doGet()和doPost()方法就行了,因为HttpServlet重写了service()方法,在service()方法中判断请求方式,根据不同的请求方式执行doXXX()方法
6、使用Idea直接创建Servlet
6.1 创建步骤
6.2 创建后的代码
web.xml代码
<!-- IDEA会自动生成servlet标签 -->
<servlet>
<servlet-name>QuickServlet</servlet-name>
<servlet-class>com.atguigu.servlet.QuickServlet</servlet-class>
</servlet>
<!-- 我们自己补充servlet-mapping标签 -->
<servlet-mapping>
<servlet-name>QuickServlet</servlet-name>
<url-pattern>/QuickServlet</url-pattern>
</servlet-mapping>
Servlet代码
public class QuickServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//在doPost()方法中调用doGet(),这样的话就只需要重写doGet()方法就能处理post和get请求
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
7、Servlet的三种映射路径的配置
7.1 映射路径的作用
Servlet的映射路径是提供一个让别人能够访问该Servlet的路径,例如Servlet的映射路径是"/hello",那么在浏览器上访问该Servlet的路径是http://localhost:8080/项目部署名/hello
注意:一个Servlet可以配置多个映射路径,但是多个Servlet不能配置相同的映射路径
7.2 映射路径的分类
7.2.1 完全路径匹配
访问当前Servlet的路径需要和配置的映射路径完全一致,例如Servlet的配置是/demo01
,那么访问该Servlet的时候的路径也必须是http://localhost:8080/项目部署名/demo01
才可以访问到
7.2.2 目录匹配
以 /
开始需要以 *
结束,注: Servlet里面用的不多, 但是过滤器里面通常就使用目录匹配
例如: 配置/* 访问的路径可写成/任意字符串,比方: /aa, /aaa;
配置/aa/* 访问的路径可写成/aa/任意字符串,比方: /aa/b , /aa/cc
7.2.3 扩展名匹配
以*
开头,以.扩展名
结束,能够匹配所有以.相同扩展名
结尾的请求路径
例如: *.action; 访问路径可以是任意字符串.action,
比方: aa.action, bb.action, c.action;
四、 ServletContext
1、ServletContext的概念
服务器为其部署的每一个应用(项目)都创建了一个ServletContext对象。ServletContext属于整个项目的,该项目中的所有Servlet都可以共享同一个ServletContext对象
2、获取ServletContext的API
2.1 调用Servlet自身的getServletContext方法获取
ServletContext ServletContext = getServletContext()
2.2 调用ServletConfig接口的getServletContext方法
ServletContext ServletContext = servletConfig.getServletContext();
//HttpServletRequest对象也实现了ServletConfig接口,所以也有getServletContext()方法
ServletContext ServletContext = request.getServletContext();
3、ServletContext获取全局的初始化参数
3.1 在web.xml中配置Web应用级别的初始化参数
<context-param>
<param-name>username</param-name>
<param-value>hahahaha</param-value>
</context-param>
3.2 在Servlet的doGet方法中获取全局参数
String username = servletContext.getInitParameter("username");
System.out.println("在ServletDemo04中获取全局的初始化参数username=" + username);
4、ServletContext作为全局的域对象
4.1 什么是域对象
域对象就是在一定的作用域范围内进行数据共享的对象,ServletContext作为全局域对象可以在整个项目的所有动态资源(包含所有Servlet)中进行数据共享
4.2 ServletContext作为域对象的API
4.2.1 往全局域对象中存入数据
servletContext.setAttribute("key",value)
4.2.2 从全局域对象中取出数据
Object value = ServletContext.getAttribute("key");
4.3 案例
4.3.1 目标
在ServletDemo01中往全局域对象中存入"username"为"aobama"的键值对,然后在ServletDemo02中从全局域对象中根据"username"获取对应的值
4.3.2 代码
ServletDemo01中的代码
//1. 获取ServletContext对象
ServletContext ServletContext = getServletContext();
//2. 存入数据
servletContext.setAttribute("username","aobama");
ServletDemo02中的代码
//1. 获取ServletContext对象
ServletContext ServletContext = getServletContext();
//2. 取出数据
String username = (String)servletContext.getAttribute("username");
//3. 打印数据
System.out.println(username)
5、获取资源的真实路径
5.1 为什么需要用代码获取资源的真实路径
例如我们的目标是需要获取项目中某个静态资源的路径,不是工程目录中的路径,而是部署目录中的路径;我们如果直接拷贝其在我们电脑中的完整路径的话其实是有问题的,因为如果该项目以后部署到公司服务器上的话,路径肯定是会发生改变的,所以我们需要使用代码动态获取资源的真实路径
5.2 获取资源真实路径的API
String realPath = servletContext.getRealPath("资源在web目录中的路径");
5.3 动态获取真实路径的优势
只要使用了servletContext动态获取资源的真实路径,那么无论项目的部署路径发生什么变化,都会动态获取项目运行时候的实际路径,所以就不会发生由于写死真实路径而导致项目部署位置改变引发的路径错误问题