Servlet
Http
Request
Response
Server Applet 数据库和程序之间的中间层。交互的桥梁,使用Servlet可以做到获取浏览器端的表单数据和获取客户端发送的一些请求信息,关联后端数据库做完业务处理,将处理后的结果值再返回给浏览器
Servlet的生命周期
-
第一个方法:init()初始化Servlet类对象信息,当Servlet对象创建的时候被调用,只会被调用一次
-
Servlet类对象什么时候被创建出来呢?
-
默认情况下,该Servlet类第一次被访问是,Servlet被创建
-
手动指定Servlet类对象的创建时机,
-
通过在
标签中配置 标签 1.第一次访问时被创建出来
标签的值为负数 2.在服务器启动的时候被创建出来
的值为0或正整数 注意:
-
load-on-startup是用来标记容器是否在启动的时候就加载这个Servlet类(实例化并执行init方法)
-
load-on-startup它的值必须是一个整数,表示这个Servlet类的加载顺序
-
当值>=0时,表示容器在应用启动的时候就加载并初始化这个Servlet类
-
当值<0的时候,表示容器在该Servlet类被选择(服务被提供)的时候才会加载
-
整数值越小,表明该Servlet类的优先级越高,应用启动时先加载
-
当值相同的时候,容器自动选择一个Servlet加载
备注:
-
Servlet接口的init方法,只会执行一次,说明一个Servlet在内存中只会存在一个对象,因此Servlet是单例的
-
狭义:一个类在内存中自始至终只有一个类对象,将其类的构造器私有化便能完成,new动作只能在类的内部使用
-
广义:只有满足在整个系统(应用)中有且仅有一个实例即可,不保证是同一个对象
-
在Servlet3.1规范中,当自定义的Servlet类没有实现SingleThreadModel接口的时候,Servlet才是单例的。如果实现该接口,每次请求该Servlet类的时候,容器都会创建一个新的Servlet对象
-
面试题:Servlet容器如何在多线程环境下访问数据的安全性问题
Servlet容器默认情况下采用单例多线程的方式处理多个请求
当web容器启动时(客户端发送请求),Servlet就会被加载并实例化
容器初始化Servlet主要就是读取配置文件web.xml,在tomcat容器中,也有一个web.xml,在server.xmlz中可以设定线程池的数量,初始化线程池通过web.xml
当客户端请求到达时,Servlet容器可以通过线程调度调度管理下的线程池中等待的空闲现线程。
线程执行Service方法
当线程执行完毕,归还线程到线程池中
当多个用户同时访问同一个Servlet类的时候,可能存在线程安全问题,解决方法:不要在Servlet中定义成员变量方法,如果定义,也不要去修该
-
-
-
-
-
-
-
第二个方法:service方法:当客户端发送请求的时候,执行,会执行多次
-
第三个方法:destroy方法:当Servlet对象被销毁,会被调用
- 当服务器正常关闭,调用destroy
- 当服务器被修改的时候,调用destroy
Servlet3.0
提供了注解配置 、
-
好处:不需要再去web.xml注册Servlet类信息
-
步骤:
- 创建一个项目时,选择版本3.0或以上
- 在该Servlet上面添加@WebServlet注解
- 在注解内配置客户端url请求资源路径
-
@WebServlet(“/请求资源路径”)
@Target({ElementType.TYPE}) //注解的注解 @Retention(RetentionPolicy.RUNTIME) @Documented public @interface WebServlet { String name() default ""; //匹配 <servlet-name> String[] value() default {}; //代表下方的 urlPatterns属性 正常写法@WebServlet(urlPatterns = “/请求资源路径”)可以简写为 @WebServlet(“/请求资源路径”) String[] urlPatterns() default {};//相当于 <url-patterns> int loadOnStartup() default -1;//服务被提供的时加载
欢迎界面
当我们向浏览器中输入我们的服务器地址,路径url格式:http://localhost:8080/day48/,此时,tomcat会自动按照在web.xml中配置的
注意:
- 访问的是当前web应用根目录下的文件资源,一般情况下访问的是web文件夹下的资源
- 但有一个文件夹下面的资源直接通过浏览器是访问不到的 WEB-INF(安全目录、保护目录)
- 只能通过服务器内部访问
- 在
标签内部配置的欢迎界面是一种服务器行为,但在浏览器客户端上直接访问是不可行的
两种访问web资源的方式:
- 内部转发(服务器行为)
- 重定向(客户端行为)
Servlet相关配置
主要配置
-
String [ ] urlPatterns():一个Servlet可以配置多个访问路径
@WebServlet({” /xxx“,” “,...})
-
路径定义的规则:(都是虚拟目录)
- /xxx:路径匹配/完全匹配
- /xxx/yyy/... :多层路径匹配/目录匹配(只是为了显示项目模块化管理的整齐)
- / 缺省配置 当访问web应用中的资源(Servlet)都不匹配,此时会找缺省配置的Servlet信息,让这个缺省的Servlet来处理这个请求,但它无法处理jsp资源
- /* 全部处理 包含静态资源和jsp资源 借助于Servlet中的访问方式:内部转发和重定向
- *.do , *.action:扩展名匹配 一般要写具体的哪一服务, 如:userEdit.do , userEdit.action
Http
Hyper Text Transfer Protocol 超文本传输协议,是互联网中应用最广泛的网络协议
传输协议:定义了客户端和服务端通信时,发送数据的格式、
-
特点
- 基于TCP / IP的协议
- 默认的端口号是80端口
- 基于请求与响应模型,一次请求对应一次响应
- 无状态的:每次请求之间相互独立,每次请求之间无法进行数据交互(请求隔离)
-
历史版本:
- 1.0
- 每一次的请求与响应都需要建立一次连接,请求与响应结束后连接随之断开,每次建立的连接都是新的连接
- 1.1 作为目前的主流版本
- 支持持久连接(版本最大变化),请求响应结束后,默认情况下是不断开的,可以被多个请求复用,不用再声明:Connection:keep-alive,
- 客户端和服务器端发现对方有一段时间没有活动,就会主动的关闭连接,如果在客户端发送最后一个请求时,这个请求发送了Connection:close,要求服务器关闭连接。
- 目前大多数浏览器,对于同一个域名最多允许同时建立6个持久连接,提高带宽利用率
- 加入管道机制,在同一个连接里面,允许同时发送多个请求,增加了并发性,改善了Http协议的效率
- 2.0
- 为了解决利用率不高的问题,继续优化,提出了2.0版本增加双工模式,不仅客户端可以同时发送多个请求,服务器也可以同时处理多个请求,解决了队头堵塞问题
- 1.0
-
协议的组成与过程
- Http协议由Http请求和Http响应组成,当在客户端中输入一个url地址,浏览器会将你的请求消息封装为一个Http请求的格式发送给服务器端,服务器端收到这个消息之后,解析这个请求消息将它装配到ServletRequest对象中,做完业务逻辑之后,组织响应数据封装为一个Http响应的格式返回给浏览器客户端,这个过程称为请求与响应,如果没有请求就没有响应。
-
抓包分析
使用Chrome自带的抓包工具可以看到数据之间传输的过程
Http请求
get请求----Http默认的请求方式(直接在客户端的地址栏中输入url和使用a标签都只能是get方式(无法指定请求方式),对于form表单可以指定请求方式)
// 请求行:请求方式 请求路径 http版本号
GET /checkUser?username=admin&password=123456 HTTP/1.1
// 本机服务器的ip地址 域名
Host: localhost:8080
// 连接的方式 长连接
Connection: keep-alive
// 告诉服务器,自己支持你这中操作,我能读懂服务器发送的的信息,建议使用https而不是用http
Upgrade-Insecure-Requests: 1
//用户使用的浏览器客户端的内核 还包括操作系统相关的信息
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
// 接收的数据格式
// 告诉服务器端浏览器客户端能够接收的哪种类型的数据
Accept:
// mime类型 大范围/小范围
text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*//*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
// 来源地--->浏览器通知服务器,当前的请求时来自于哪个地方
// Referer:经常用于防盗链 防止盗取我的连接信息 如果你是
//___直接请求资源,没有referer___
Referer: http://localhost:8080/
// 接收的压缩格式 浏览器通知服务器我可以接收哪些类型压缩格式的数据
Accept-Encoding: gzip, deflate, br
// 浏览器可支持的语言
Accept-Language: zh-CN,zh;q=0.9
// Cookie JSESSIONID
Cookie: Idea-ac70df0d=96318eda-906d-4909-81b5-89db73f5358a; JSESSIONID=43EE4E93151302CD4B10579871CB8328
post请求
POST /checkUser HTTP/1.1
Host: localhost:8080
Connection: keep-alive
// 内容的长度
Content-Length: 30
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
// 如果是post请求在请求体中的数据使用url编码
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*//*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: Idea-ac70df0d=96318eda-906d-4909-81b5-89db73f5358a; JSESSIONID=43EE4E93151302CD4B10579871CB8328
// 请求体内容
username=admin&password=123456
Http响应
// 响应行 http协议 状态码
HTTP/1.1 200
// 响应头
// 响应正文的内容长度 当前为0
Content-Length: 0
// 响应的时间
Date: Tue, 12 Jan 2021 07:49:14 GMT
Keep-Alive: timeout=20
Connection: keep-alive
常用的状态码
常用状态码 | 描述 |
---|---|
200 | 请求成功 |
302 | 重定向 |
304 | 读取本地缓存文件 |
404 | 请求的资源找不到 |
405 | 请求的方式不对,如客户端使用get请求 服务器使用post接收 |
500 | 服务器端程序代码错误 |
Servlet体系结构
在实际开发中,创建一个Servlet实现类大多使用的是service()方法,其他方法并没有调用。由于必须遵从Servlet接口规范,不得不重写大量无关方法。
在Servlet体系结构中,HttpServlet将其他无关的抽象方法全部封装,我们今后可以定义它的子类来实现遵守Servlet接口规范
继承实现体系:
Servlet (接口)
GenericServlet (如果创建该类子类,需要重写service方法,还需要判断请求类型)
HttpServlet(直接继承使用)
GenericServlet 将Servlet接口中的抽象方法基本实现,但是并未实现service方法,需要子类来实现,但是客户端发送请求时需要区别请求方式的不同,在service方法中还需判断。很不方便,在它的子类HttpServlet中则实现了service方法,对七种请求方式做了区分操作。很方便
- 查看源码才发现doGet和doPost方法中的两个参数分别是HttpServletRequest和HttpServletResponse,就是我们需要的请求对象和响应对象(自动转换处理)
- request和response内部原理,参照客户端和服务器端发送数据的原理
Request对象
Request常用的API
Request对象的类型是HttpServletRequest,该类中定义了很多与http协议相关的方法,比如获取请求头信息,请求方式,客户端ip地址等信息。下面是常用的API.
(1)常用信息
String getRemoteAddr():获取客户端ip地址
String getMethod():获取客户端请求方式。例如:get或post
(2)获取请求头信息
String getHeader(String name):获取单值的请求头的值。
int getIntHeader(String name):获取单值int类型的请求头的值
Request获取请求参数方法
方法名 | 方法介绍 |
---|---|
String getParameter(String name ) | 获取指定名称的请求参数值,适用于单值的请求参数 |
String[] getParameterValues(String name) | 获取指定名称的请求参数值,适用于多值的请求参数 |
Enumeration |
获取所有的请求参数名称 |
Map<String,String[]> getParameterMap() | 获取所有请求参数,其中参数名作为map的key,参数值作为map的value. |
Request获取请求路径方法介绍
Request对象中包含的是请求信息。下面以一个路径为例子,为大家展示请求路径的几个方法。
地址:http://localhost:8080/Test/login?username=zhangsan.
Request对象通过以下方法来获取请求路径。
(1)String getServerName():获取服务器名:localhost
(2)String getServerPort():获取服务器端口号:8080
(3)String getContextPath():获取项目名:Test
(4)String getServletPath():获取Servlet路径:/login
(5)String getQueryString():获取参数部门,即问号后面的部分:username=zhangsan
(6)String getRequestURL():获取请求URL
//获取请求方式
String method = req.getMethod();
System.out.println("请求方式"+method+"------------");
//获取虚拟目录
String contextPath = req.getContextPath();
System.out.println("虚拟目录"+contextPath+"-------------");
//获取请求资源路径
String servletPath = req.getServletPath();
System.out.println("请求资源路径"+servletPath+"-------------");
//获取get方式请求参数信息
String queryString = req.getQueryString();
System.out.println("请求参数信息"+queryString+"--------------");
//获取URI和URL
String requestURI = req.getRequestURI();
System.out.println("URI"+requestURI+"-------------");
StringBuffer requestURL = req.getRequestURL();
System.out.println("URL"+requestURL+"--------------");
//获取浏览器客户端的ip地址
String remoteAddr = req.getRemoteAddr();
System.out.println("客户端的ip地址"+remoteAddr+"-------------");