HTTP概念
HyperText Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则。
F12
打开开发者工具,点击Network
来查看某一次请求的请求数据和响应数据具体的格式内容所以学习HTTP主要就是学习请求和响应数据的具体格式内容。
HTTP协议特点
HTTP协议有它自己的一些特点,分别是:
基于TCP协议: 面向连接,安全
TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全。
基于请求-响应模型的:一次请求对应一次响应
请求和响应是一一对应关系
HTTP协议是无状态协议:对于事物处理没有记忆能力。每次请求-响应都是独立的
无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。这种特性有优点也有缺点,
请求之间无法共享数据会引发的问题,如:
加入购物车
和去购物车结算
是两次请求,请求数据总共分为三部分内容,分别是请求行、请求头、请求体
请求行: HTTP请求中的第一行数据,请求行包含三块内容,分别是 GET[请求方式] /[请求URL路径] HTTP/1.1[HTTP协议及版本]
请求方式有七种,最常用的是GET和POST
请求头: 第二行开始,格式为key: value形式
请求头中会包含若干个属性,常见的HTTP请求头有:
Host: 表示请求的主机名 User-Agent: 浏览器版本,例如Chrome浏览器的标识类似Mozilla/5.0 ...Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT ...)like Gecko; Accept:表示浏览器能接收的资源类型,如text/*,image/*或者*/*表示所有; Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页; Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip, deflate等。
这些数据有什么用处?
举例说明:服务端可以根据请求头中的内容来获取客户端的相关信息,有了这些信息服务端就可以处理不同的业务需求,比如:
请求体: POST请求的最后一部分,存储请求参数
如上图红线框的内容就是请求体的内容,请求体和请求头之间是有一个空行隔开。此时浏览器发送的是POST请求,为什么不能使用GET呢?这时就需要回顾GET和POST两个请求之间的区别了:
响应数据总共分为三部分内容,分别是响应行、响应头、响应体
响应行:响应数据的第一行,响应行包含三块内容,分别是 HTTP/1.1[HTTP协议及版本] 200[响应状态码] ok[状态码的描述]
响应头:第二行开始,格式为key:value形式
响应头中会包含若干个属性,常见的HTTP响应头有:
Content-Type:表示该响应内容的类型,例如text/html,image/jpeg; Content-Length:表示该响应内容的长度(字节数); Content-Encoding:表示该响应压缩算法,例如gzip; Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒
响应体: 最后一部分。存放响应数据
上图中...这部分内容就是响应体,它和响应头之间有一个空行隔开。
参考:https://cloud.tencent.com/developer/chapter/13553
Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术。
使用Servlet就可以实现,根据不同的登录用户在页面上动态显示不同内容。
Servlet是JavaEE规范之一,其实就是一个接口,将来我们需要定义Servlet类实现Servlet接口,并由web服务器运行Servlet
http://localhost:8080/web-demo/demo1
请求,从请求中可以解析出三部分内容,分别是localhost:8080
、web-demo
、demo1
localhost:8080
可以找到要访问的Tomcat Web服务器web-demo
可以找到部署在Tomcat服务器上的web-demo项目demo1
可以找到要访问的是项目中的哪个Servlet类,根据@WebServlet后面的值进行匹配小结
介绍完Servlet的执行流程,需要大家掌握两个问题:
Servlet由web服务器创建,Servlet方法由web服务器调用
因为我们自定义的Servlet,必须实现Servlet接口并复写其方法,而Servlet接口中有service方法
介绍完Servlet的执行流程后,我们知道Servlet是由Tomcat Web服务器帮我们创建的。
接下来咱们再来思考一个问题:Tomcat什么时候创建的Servlet对象?
要想回答上述问题,我们就需要对Servlet的生命周期进行一个学习。
生命周期: 对象的生命周期指一个对象从被创建到被销毁的整个过程。
Servlet运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:
默认情况,Servlet会在第一次访问被容器创建,但是如果创建Servlet比较耗时的话,那么第一个访问的人等待的时间就比较长,用户的体验就比较差,那么我们能不能把Servlet的创建放到服务器启动的时候来创建,具体如何来配置? @WebServlet(urlPatterns = "/demo1",loadOnStartup = 1) loadOnstartup的取值有两类情况 (1)负整数:第一次访问时创建Servlet对象 (2)0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高
/** * Servlet生命周期方法 */ @WebServlet(urlPatterns = "/demo2",loadOnStartup = 1) public class ServletDemo2 implements Servlet { /** * 初始化方法 * 1.调用时机:默认情况下,Servlet被第一次访问时,调用 * * loadOnStartup: 默认为-1,修改为0或者正整数,则会在服务器启动的时候,调用 * 2.调用次数: 1次 * @param config * @throws ServletException */ public void init(ServletConfig config) throws ServletException { System.out.println("init..."); } /** * 提供服务 * 1.调用时机:每一次Servlet被访问时,调用 * 2.调用次数: 多次 * @param req * @param res * @throws ServletException * @throws IOException */ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { System.out.println("servlet hello world~"); } /** * 销毁方法 * 1.调用时机:内存释放或者服务器关闭的时候,Servlet对象会被销毁,调用 * 2.调用次数: 1次 */ public void destroy() { System.out.println("destroy..."); } public ServletConfig getServletConfig() { return null; } public String getServletInfo() { return null; } }
默认是第一次访问的时候被创建,可以使用@WebServlet(urlPatterns = "/demo2",loadOnStartup = 1)的loadOnStartup 修改成在服务器启动的时候创建。
涉及到三个方法,分别是 init()、service()、destroy()
init方法在Servlet对象被创建的时候执行,只执行1次
service方法在Servlet被访问的时候调用,每访问1次就调用1次
destroy方法在Servlet对象被销毁的时候调用,只执行1次
Servlet中总共有5个方法,
void init(ServletConfig config)
void service(ServletRequest req, ServletResponse res)
void destroy()
String getServletInfo() //该方法用来返回Servlet的相关信息,没有什么太大的用处,一般我们返回一个空字符串即可 public String getServletInfo() { return ""; }
ServletConfig getServletConfig()
ServletConfig对象,在init方法的参数中有,而Tomcat Web服务器在创建Servlet对象的时候会调用init方法,必定会传入一个ServletConfig对象,我们只需要将服务器传过来的ServletConfig进行返回即可。
private ServletConfig servletConfig; /** * 初始化方法 * 1.调用时机:默认情况下,Servlet被第一次访问时,调用 * * loadOnStartup: 默认为-1,修改为0或者正整数,则会在服务器启动的时候,调用 * 2.调用次数: 1次 * @param config * @throws ServletException */ public void init(ServletConfig config) throws ServletException { this.servletConfig = config; System.out.println("init..."); } public ServletConfig getServletConfig() { return servletConfig; } public String getServletInfo() { return ""; }
开发B/S架构的web项目,都是针对HTTP协议,所以我们自定义Servlet,会通过继承HttpServlet
具体的编写格式如下:
@WebServlet("/demo4") public class ServletDemo4 extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //TODO GET 请求方式处理逻辑 System.out.println("get..."); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //TODO Post 请求方式处理逻辑 System.out.println("post..."); } }
http://localhost:8080/web-demo/demo4
,就能看到doGet方法被执行了a.html
页面,内容如下:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/web-demo/demo4" method="post"> <input name="username"/><input type="submit"/> </form> </body> </html>
启动测试,即可看到doPost方法被执行了。
Servlet类编写好后,要想被访问到,就需要配置其访问路径(urlPattern)
/** * urlPattern: 一个Servlet可以配置多个访问路径 */ @WebServlet(urlPatterns = {"/demo7","/demo8"}) public class ServletDemo7 extends MyHttpServlet { @Override protected void doGet(ServletRequest req, ServletResponse res) { System.out.println("demo7 get..."); } @Override protected void doPost(ServletRequest req, ServletResponse res) { } }
在浏览器上输入http://localhost:8080/web-demo/demo7
,http://localhost:8080/web-demo/demo8
这两个地址都能访问到ServletDemo7的doGet方法。
urlPattern配置规则
精确匹配
/** * UrlPattern: * * 精确匹配 */ @WebServlet(urlPatterns = "/user/select") public class ServletDemo8 extends MyHttpServlet { @Override protected void doGet(ServletRequest req, ServletResponse res) { System.out.println("demo8 get..."); } @Override protected void doPost(ServletRequest req, ServletResponse res) { } }
访问路径http://localhost:8080/web-demo/user/select
目录匹配
/** * UrlPattern: * * 目录匹配: /user/* */ @WebServlet(urlPatterns = "/user/*") public class ServletDemo9 extends MyHttpServlet { @Override protected void doGet(ServletRequest req, ServletResponse res) { System.out.println("demo9 get..."); } @Override protected void doPost(ServletRequest req, ServletResponse res) { } }
访问路径http://localhost:8080/web-demo/user/任意
/*
代表的是零或多个层级访问目录同时精确匹配优先级要高于目录匹配。
扩展名匹配
/** * UrlPattern: * * 扩展名匹配: *.do */ @WebServlet(urlPatterns = "*.do") public class ServletDemo10 extends MyHttpServlet { @Override protected void doGet(ServletRequest req, ServletResponse res) { System.out.println("demo10 get..."); } @Override protected void doPost(ServletRequest req, ServletResponse res) { } }
访问路径http://localhost:8080/web-demo/任意.do
注意:
如果路径配置的不是扩展名,那么在路径的前面就必须要加/
否则会报错
如果路径配置的是*.do
,那么在*.do的前面不能加/
,否则会报错
任意匹配
/** * UrlPattern: * * 任意匹配: / */ @WebServlet(urlPatterns = "/") public class ServletDemo11 extends MyHttpServlet { @Override protected void doGet(ServletRequest req, ServletResponse res) { System.out.println("demo11 get..."); } @Override protected void doPost(ServletRequest req, ServletResponse res) { } }
访问路径http://localhost:8080/demo-web/任意
/** * UrlPattern: * * 任意匹配: /* */ @WebServlet(urlPatterns = "/*") public class ServletDemo12 extends MyHttpServlet { @Override protected void doGet(ServletRequest req, ServletResponse res) { System.out.println("demo12 get..."); } @Override protected void doPost(ServletRequest req, ServletResponse res) { } }
访问路径`http://localhost:8080/demo-web/任意
注意:/
和/*
的区别?
当我们的项目中的Servlet配置了 "/",会覆盖掉tomcat中的DefaultServlet,当其他的url-pattern都匹配不上时都会走这个Servlet
当我们的项目中配置了"/*",意味着匹配任意访问路径
DefaultServlet是用来处理静态资源,如果配置了"/"会把默认的覆盖掉,就会引发请求静态资源的时候没有走默认的而是走了自定义的Servlet类,最终导致静态资源不能被访问
小结
urlPattern总共有四种配置方式,分别是精确匹配、目录匹配、扩展名匹配、任意匹配
五种配置的优先级为 精确匹配 > 目录匹配> 扩展名匹配 > /* > / ,无需记,以最终运行结果为准。
对应Servlet的配置,使用的是@WebServlet,这个是Servlet从3.0版本后开始支持注解配置,3.0版本前只支持XML配置文件的配置方法。
对于XML的配置步骤有两步:
public class ServletDemo13 extends MyHttpServlet { @Override protected void doGet(ServletRequest req, ServletResponse res) { System.out.println("demo13 get..."); } @Override protected void doPost(ServletRequest req, ServletResponse res) { } }
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- Servlet 全类名 --> <servlet> <!-- servlet的名称,名字任意--> <servlet-name>demo13</servlet-name> <!--servlet的类全名--> <servlet-class>com.itheima.web.ServletDemo13</servlet-class> </servlet> <!-- Servlet 访问路径 --> <servlet-mapping> <!-- servlet的名称,要和上面的名称一致--> <servlet-name>demo13</servlet-name> <!-- servlet的访问路径--> <url-pattern>/demo13</url-pattern> </servlet-mapping> </web-app>