目录
- Servlet
-
- Servlet做了什么
- JSP与Servlet的关系
- [主要Servlet API介绍](#主要Servlet API介绍)
- Servlet生命周期
- ServletConfig类
- ServletContext类
- HTTP协议
- 过滤器
- 总结:执行原理
Servlet
- Server+Applet,是一种服务器端的Java应用程序
- 只有当一个服务器端的程序使用了Servlet API的时候,这个服务端的程序才能称之为Servlet
- Servlet是JavaWeb三大组件之一,三大组件是:Servlet程序、Filter过滤器、Listener监听器
- Servlet是运行在服务器上的一个java小程序,它可以接受客户端发送过来的请求,并响应数据给客户端
Servlet做了什么
- 本身不做任何业务处理
- 只是接收请求并决定调用哪个JavaBean去处理请求
- 确定用哪个页面来显示处理返回的数据
JSP与Servlet的关系
Servlet是运行在服务器端的Java应用程序
主要Servlet API介绍
- javax.servlet.Servlet接口
所有Java Servlet的基础接口类,规定了必须由Servlet具体类实现的方法集 - javax.servlet.GenericServlet类
是Servlet的通用版本,是一种与协议无关的Servlet - javax.servlet.http.HttpServlet类
在GenericServlet基础上扩展的基于Http协议的Servlet
如何创建Servlet
- 实现Servlet接口
- 继承GenericServlet类
- 继承HttpServlet类
Servlet中主要方法
- init():Servlet的初始化方法,仅仅会执行一次
- service():处理请求和生成响应
- destroy():在服务器停止并且程序中的Servlet对象不再使用的时候调用,只执行一次
ServletRequest
- 封装客户的请求信息
- 作用相当于JSP内置对象request
ServletResponse
- 创建响应信息,将处理结果返回给客户端
- 作用相当于JSP内置对象response
ServletConfig
- 包含了Servlet的初始化参数信息
Servlet生命周期
- 加载和实例化
- 初始化
- 处理请求
- 销毁
生命周期 | 谁来做 | 何时做 |
---|---|---|
实例化 | Servlet 容器 | 当Servlet容器启动或者容器检测到客户端请求时 |
初始化 | Servlet 容器 | 实例化后,容器调用Servlet的init()初始化对象 |
处理请求 | Servlet 容器 | 得到客户端请求并做出处理时 |
销毁 | Servlet 容器 | 当程序中的Servlet对象不再使用的时候,或者Web服务器停止运行的时候 |
Servlet创建
java
package com.zjl.servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author: zjl
* @datetime: 2024/4/10
* @desc:
*/
public class HelloServlet extends HttpServlet {
public HelloServlet() {
System.out.println("生命周期之------------------创建实例");
}
@Override
public void init(ServletConfig config) throws ServletException {
String mycharset = config.getInitParameter("mycharset");
System.out.println("初始化时加载配置中的参数为:" + mycharset);
System.out.println("生命周期之------------------初始化");
}
//接收Get请求
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("生命周期之------------------处理请求");
req.setCharacterEncoding("UTF-8");//设置请求字符集编码
resp.setCharacterEncoding("UTF-8");//设置响应字符集编码
req.setAttribute("key","value");//将数据保存在request域,以键值对方式
req.getSession().setAttribute("sessionKey","sessionValue");//根据request获取所在的session会话,并向会话保存一个数据
req.getRequestDispatcher("welcome.jsp").forward(req,resp);//转发跳转页面
}
//接收POST请求
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("生命周期之------------------处理请求");
}
/*@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("生命周期之------------------处理请求");
}*/
@Override
public void destroy() {
System.out.println("生命周期之------------------销毁");
}
}
Servlet部署与运行
- 修改web.xml(部署描述文件)
- 添加:把Servlet内部名映射到一个Servlet类名
- 添加:把用户访问的URL映射到Servlet的内部名
xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.zjl.servlet.HelloServlet</servlet-class>
<init-param>
<param-name>mycharset</param-name>
<param-value>utf-t</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello.do</url-pattern>
</servlet-mapping>
</web-app>
- 通过URL访问Servlet
ServletConfig类
- ServletConfig类从类名上来看,就知道是Servlet程序的配置信息类
- Servlet程序和ServletConfig对象都是由Tomcat负责创建,我们负责调用
- Servlet程序默认是第一次访问的时候创建,ServletCongfig是每个Servlet程序创建时,就创建一个对应的ServletConfig对象
ServletConfig类的三大作用
- 可以获取Servlet程序的别名servlet-name的值
- 获取初始化参数init-param
- 获取ServletContext对象
java
@Override
public void init(ServletConfig config) throws ServletException {
String mycharset = config.getInitParameter("mycharset");
System.out.println("初始化时加载配置中的参数为:" + mycharset);
System.out.println("生命周期之------------------初始化");
config.getServletContext();
config.getServletName();
}
ServletContext类
- ServletContext是一个接口,它表示Servlet上下文对象
- 一个web工程,只有一个ServletContext对象实例
- ServletContext对象是一个域对象
- ServletContext是在web工程部署启动的时候创建,在web工程停止的时候销毁
域对象:是可以像Map一样存取数据的对象
这里的域指的是存取数据的操作范围是整个web工程
存数据 | 取数据 | 删除数据 |
---|---|---|
Map | put() | get() |
域对象 | setAttribute() | getAttribute() |
ServletContext类的四个作用
- 获取web.xml中配置的上下文参数context-param
属于整个工程,也就是说所有的Servlet都可以访问到
xml
<!--context-param是上下文参数(它属于整个web工程)-->
<context-param>
<param-name>username</param-name>
<param-value>context</param-value>
</context-param>
<!--context-param是上下文参数(它属于整个web工程)-->
<context-param>
<param-name>password</param-name>
<param-value>root</param-value>
</context-param>
- 获取当前的工程路径,格式:/工程路径
java
//2、获取当前的工程路径,格式: /工程路径
System.out.println("当前工程路径:" + servletContext.getContextPath());
- 获取工程部署后在服务器硬盘上的绝对路径
java
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取工程部署后在服务器硬盘上的绝对路径
/**
* / 斜杠被服务器解析地址为http://ip:port/工程名/ 映射到IDEA代码的web目录
*/
System.out.println("工程部署的路径是:" + servletContext.getRealPath("/"));
System.out.println("工程下imgs目录的绝对路径是:" + servletContext.getRealPath("/imgs"));
System.out.println("工程下imgs目录下1.jpg的绝对路径是:" + servletContext.getRealPath("/imgs/1.jpg"));
}
- 像Map一样存取数据
- 当我们存储之前,还未存储的时候,此时去获取数据,是获取不到即返回null
- 一个web工程,只有一个ServletContext对象实例,因此地址一致
java
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取ServletContext对象
ServletContext servletContext = getServletContext();
// System.out.println(servletContext);
System.out.println("保存之前Context1 中获取域数据key1的值是:" + servletContext.getAttribute("key1"));
servletContext.setAttribute("key1", "value1");
System.out.println("Context1 中获取域数据key1的值是:" + servletContext.getAttribute("key1"));
}
HTTP协议
- 协议是指双方、或多方,相互约定好的,大家都需要遵守的规则,叫协议
- 所谓HTTP协议,就是指:客户端和服务端之间通信时,发送的数据,需要遵守的规则,叫HTTP协议
- HTTP协议中的数据又叫报文
- 客户端给服务器发送数据叫请求
- 服务器给客户端回传数据叫响应
GET请求和POST请求
GET
格式
- 请求行
请求的方式
请求的资源路径[+?+请求参数]
请求的协议的版本号 - 请求头
key : value 组成,不同的键值对,表示不同的含义
POST
格式
- 请求行
请求的方式
请求的资源路径[+?+请求参数]
请求的协议的版本号 - 请求头
key : value 组成,不同的键值对,表示不同的含义 - 请求体 ==> 就是发送给服务器的数据
常用的请求头的说明
- Accept:表示客户端可以接受的数据类型
- Accpet-Language:表示客户端可以接受的语言类型
- User-Agent:表示客户端浏览器的信号
- Host:表示请求时的服务器ip和端口号
GET请求和POST请求的种类(即哪些是)
GET请求有:
- form 标签 method="get"
- a 标签
- link 标签引入 css 文件
- Script 标签引入 js 文件
- img 标签引入图片
- iframe 引入 html 页面
- 在浏览器地址栏中输入地址后敲回车
POST请求有:
- form 标签 method="post"
响应的HTTP协议格式
-
响应行
响应的协议和版本号
响应状态码
响应状态描述符
-
响应头
key : value 不同的响应头,有其不同的含义
-
响应体 ==> 就是回传给客户端的数据
常用的响应码
响应码 | 说明 |
---|---|
200 | 表示请求成功 |
302 | 表示请求重定向 |
404 | 表示请求服务器以及收到了,但是你要的数据不存在(请求地址错误) |
500 | 表示服务器已经收到请求,但是服务器内部错误(代码错误) |
MIME类型说明
- MIME是HTTP协议中的数据类型
- MIME 的英文全称是"Multipurpose Internet Mail Extensions" 多功能 Internet 邮件扩充服务。MIME 类型的格式是"大类型/小类型",并与某一种文件的扩展名相对应
常见的MIME类型
文件 | MIME类型 |
---|---|
超文本标记语言文本 | .html, .htm text/html |
普通文本 .txt text/plain | |
RTF文本 | .rtf application/rtf |
GIF图形 | .gif image/gif |
JDEG图形 | .jpeg, .jpg image/jpeg |
au声音文件 | .au audio/basic |
MIDI音乐文件 | .mid, .midi audio.midi, audio/x-midi |
RealAudio音乐文件 | .ra, .ram audio/x-pn-realaudio |
MPEG文件 | .mpg, .mpeg video/mpeg |
AVI文件 | .avi video/x-msvideo |
GZIP文件 | .gz application/x-gzip |
TAR文件 | .tar application/x-tar |
HttpServletRequest类
- 对应JSP的内置对象request
- 每次只要有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的HTTP协议信息解析好封装到Request对象中,然后传递到service方法或doGet方法或doPost方法中给我们使用。而我们可以通过HttpServletRequest对象,获取到所有请求的信息
- 普通的访问无法访问到WEB-INF目录下的内容,但是请求转发可以访问到
java
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/WEB-INF/form.html");
- 无法访问工程以外的资源,因为默认放本工程目录下
java
RequestDispatcher requestDispatcher = req.getRequestDispatcher("http://www.baidu.com");//会报错
常用方法
方法名 | 作用 |
---|---|
getRequestURI() | 获取请求的资源路径 |
getRequestURL() | 获取请求的统一资源定位符(绝对路径) |
getRemoteHost() | 获取客户端的ip地址 |
getHeader() | 获取请求头 |
getParameter() | 获取请求的参数 |
getParameterValues() | 获取请求的参数(多个值的时候使用)[如复选框] |
getMethod() | 获取请求的方式GET或POST |
setAttribute(key, value) | 设置域数据 |
getAttribute(key) | 获取域数据 |
getRequestDispatcher() | 获取请求转发对象 |
HttpServletResponse类
- 对应JSP内置对象response
- HttpServletResponse类和HttpServletRequest类一样,每次请求进来,Tomcat服务器都会创建一个Response对象传递给Servlet程序去使用。HttpServletRequest表示请求过来的信息,HttpServletResponse表示所有响应的信息
- 我们如果需要设置返回给客户端的信息,都可以通过HttpServletResponse对象来进行设置
- 不共享Request域中数据
- 不能访问WEB-INF下的资源。是因为WEB-INF是受保护的,而浏览器是不能够访问受保护的,当重新第二次到新地址,也是浏览器发送请求,因此不能访问到受保护的WEB-INF目录
- 可以访问工程下的资源
类别 | 表示 | 作用 |
---|---|---|
字节流 | getOutputStream() | 常用于下载(传递二进制数据) |
字符流 | getWriter() | 常用于回传字符串(常用) |
注意:两个流同时只能使用一个
使用了字节流,就不能再使用字符流了,反之亦然,否则就会报错
@WebServlet注解
-
在Servlet中,设置了@WebServlet注解,当请求该Servlet时,服务器就会自动读取当中的信息
-
如果注解@WebServlet("/category"),则表示该Servlet默认的请求路径为.../category,这里省略了urlPatterns属性名,完整的写法应该是:@WebServlet(urlPatterns = "/category")
-
如果在@WebServlet中需要设置多个属性,必须给属性值加上属性名称,中间用逗号隔开,否则会报错.
-
若没有设置@WebServlet的name属性,默认值会是Servlet的类完整名称.
-
在servlet3.0以后,web.xml中对Servlet配置,同样可以在@WebServlet注解中配置.
属性列表
属性名 | 类型 | 描述 |
---|---|---|
name | String | 指定Servlet 的 name 属性,等价于 。如果没有显式指定,则该 Servlet 的取值即为类的全限定名。 |
value | String[] | 该属性等价于 urlPatterns 属性。两个属性不能同时使用。 |
urlPatterns | String[] | 指定一组 Servlet 的 URL 匹配模式。等价于标签。 |
loadOnStartup | int | 指定 Servlet 的加载顺序,等价于 标签。 |
initParams | WebInitParam[] | 指定一组 Servlet 初始化参数,等价于标签。 |
asyncSupported | boolean | 声明 Servlet 是否支持异步操作模式,等价于 标签。 |
description | String | 该 Servlet 的描述信息,等价于 标签。 |
displayName | String | 该 Servlet 的显示名,通常配合工具使用,等价于 标签。 |
过滤器
拦截所有访问web资源的请求或者响应(servlet、Jsp页面、HTML页面),从而实现我们自己的业务逻辑,这些逻辑可以是实现访问权限的控制、过滤敏感词、压缩响应等功能。
- 是向Web应用程序的请求和响应添加功能的Web服务组件
- 过滤器可以统一地集中处理请求和响应
- 使用过滤器技术实现对请求数据的过滤
原理
过滤器是"链接"在容器的处理过程中的,它会在servlet处理器之前访问进入的请求,并且在响应信息返回客服端之前访问这些响应信息。这样就可以动态的修改请求和响应中的内容。
代码
java
package com.zjl.filter;
import javax.servlet.*;
import java.io.IOException;
/**
* @author: zjl
* @datetime: 2024/4/10
* @desc:
*/
public class MyCharsetFilter implements Filter {
private String encode;
public MyCharsetFilter(){
System.out.println("生命周期之------创建");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
encode = filterConfig.getInitParameter("encode");
System.out.println("生命周期之------初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("生命周期之------执行过滤");
servletResponse.setCharacterEncoding(encode);
servletRequest.setCharacterEncoding(encode);
filterChain.doFilter(servletRequest,servletResponse);//执行过滤
}
@Override
public void destroy() {
System.out.println("生命周期之------销毁");
}
}
配置
xml
<filter>
<filter-name>mycharset</filter-name>
<filter-class>com.zjl.filter.MyCharsetFilter</filter-class>
<init-param>
<param-name>encode</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>mycharset</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
生命周期
- 实例化
- 初始化:init()
- 过滤:doFilter()
- 销毁:destroy()
destroy()
- 当web服务器调用该方法时,表示过滤器将被销毁。
init()
- 当web服务器调用该方法时,表示过滤器将被注册到服务中
doFilter()
- 当服务器调用Filter中的doFilter()方法,它会将每一个请求或者响应传递给下一个资源
备注
- Filter是在应用启动是被创建和初始化的。
- Filter是单例多线程的(创建和初始化只会被执行一次)
- doFilter()方法无论是那一个线程访问,只要由该Filter进行过滤,那么就会执行该Filter的doFilter()方法,并且是每过滤一次就会执行一次doFilter()。
- Filter中destroy方法是在应用被停止时调用的,它意味着销毁这个Filter。
- FilterConfig指的是Filter在web.xml中的注册信息 ,它将注册信息进行封装,然后通过形参的方式传给初始化方法
<filter-mapping>
- <filter-mapping>标签表示拦截匹配,也就是要拦截那些请求。
- <filter-name>:要拦截的过滤器名称
- <url-pattern>:拦截那些路径
<url-pattern>
-
注意Filter中在写拦截所有路径的时候只能写成/*,而不能写成/,因为写成/它就不走拦截器中的doFilter方法了。
-
在Servlet中/*即会拦截动态资源又会拦截静态资源,而/不会拦截动态资源
Filter里面的标签
- dispatcher表示分发器,表示过滤器所拦截的资源被servlet容器调用的方式,可以是REQUEST,INCLUDE,FORWARD,ERROR中的任何一个,默认是REQUEST。
- 用户可以设置多个<dispatcher>子元素用来指定过滤器对资源的多种调用方式进行拦截。
属性 | 作用 |
---|---|
FORWARD | 表示当前过滤器只会拦截由一个Servlet通过RequestDispatcher的forward()完成跳转 |
INCLUDE | 表示当前过滤器只会拦截由一个Servlet通过RequestDispatcher的include()完成跳转 |
REQUEST | 表示当前过滤器会拦截普通请求,但对于forward()与include()的跳转不进行拦截,REQUEST是默认的。 |
ERROR | 表示当跳转到指定的错误处理页面时,这个跳转请求会被当前过滤器拦截 |
过滤器链
多个过滤器会形成过滤器链
总结:执行原理
Servlet的执行原理:
-
在Servlet中有两个Map,这两个Map的key均为Servlet注册时的值,但value是不同的。第一个Map的value是Servlet实例对象的引用,第二个Map的value为的值,即Servlet类的全限定类名。
-
当对Servlet的请求到达Servlet容器时,会先对请求进行解析,使用该解析出的URL,作为比较对象,从第一个Map中查找是否有匹配的key,若不存在匹配的key,那么读取其value,即Servlet对象的引用,执行该Servlet的service()方法。
-
若不存在匹配的key ,那么再从第二个Map中查找是否有匹配的key。若存在,这读取其value,即读取其value,即要访问的Servlet的全限定类名。然后使用反射机制创建该Servlet实例,并将该实例写入到第一个Map中,然后在执行该Servlet的service()方法。
-
若第二个Map中也没有找到匹配的key,那么就跳转到错误处理页面404。
Filter的执行原理:
-
一个数组与一个Map :
- 一个Map:Map的key为的值,value为Filter实例对象的引用
- 一个数组:存在着与请求相匹配的所有Filter
-
当对某资源的请求到达Web容器时,会先对请求进行解析,使用解析出的URI作为比较对象,从Map中查找是否存在相匹配的key。若存在,那么读取其value,即Filter对象的引用,将该应用存入到数组中。然后继续向后查找,直到将Map查找完毕。这样在数组中就会存在按照查找顺序排好序的Filter引用。
-
数组初始化完毕后,开始按照数组元素顺序进行执行。所有数组中的Filter全部执行完毕后,再跳转到请求的目标资源。