过滤器&监听器&全局字符修改案例&用户权限过滤案例&装饰者模式过滤敏感词汇&统计当前网站在线人数
回顾
- JSP的页面脚本元素
组成部分 | 语法格式 |
---|---|
JSP代码片段 | <% Java代码 %> |
JSP声明 | <%! 声明全局变量 %> |
JSP脚本表达式 | <%= 变量值 %> |
注释 | <%-- --%> |
2. EL表达式如何获取不同类型的数据
EL表达式获取不同数据 | 说明 |
---|---|
获取JavaBean的属性值 | ${对象名.属性名} |
获取数组和List中的值 | ${集合或数组[索引]} |
获取Map中的值 | ${map.键} 或 ${map[“键”]} |
- 判断标签
<c:if test="${判断条件}">
</c:if>
- 多分支标签
<c:choose>
<c:when test="${判断条件}">
</c:when>
<c:when test="${判断条件}">
</c:when>
<c:otherwise>
</c:otherwise>
</c:choose>
- 遍历标签 forEach
属性名 | 属 性 描 述 |
---|---|
var | 每个要遍历的元素,放在页面域中 |
varStatus | 变量的状态对象,包含了四个属性:index, count, first, last |
items | 要遍历的集合或数组,放在作用域中 |
begin | 从哪个元素开始遍历 |
end | 到哪个元素结束 |
step | 步长,每次跨几个元素 |
- 什么是MVC
MVC | 描述 | Java Web的实现技术 |
---|---|---|
M | Model:模型 | JavaBean, Service, Dao |
V | View:视图 | JSP, JSTL, EL |
C | Controller:控制器 | Servlet 1. 获取用户提交的参数值 2. 调用业务层的方法 3. 控制页面的跳转 |
学习目标
-
过滤器
- 能够说出过滤器的作用
- 能够编写过滤器
- 能够说出过滤器生命周期相关方法
- 能够根据过滤路径判断指定的过滤器是否起作用
- 能够说出什么是过滤器链
- 能够编写过滤器解决全局乱码
-
监听器
- 能够说出监听器的作用
- 能够使用ServletContextListener监听器
学习内容
1. 过滤器的基本概念
目标
-
过滤器的概念
-
JavaWeb的三种组件
-
过滤器的使用场景
Java Web的三种基本组件
组件 | 作用 |
---|---|
Servlet | 运行在Web容器中Java程序,在MVC中做的控制器使用,生成动态网页。 |
过滤器Filter | 用来处理一些公共的,通用的功能 1. 拦截用户的请求 2. 修改用户的请求和响应 |
监听器Listener | 对作用域进行监听 1. 监听作用域的创建和销毁 2. 监听作用域中属性的变化 |
过滤器所处的位置
过滤器的使用场景:
-
可以集中处理汉字乱码的问题,把处理乱码的代码写在过滤器中,让所有通过这个过滤器的请求都没有乱码的问题
-
用户登录权限的判断,只需要在过滤器中判断请求,如果登录了就放行,没有登录就拦截。
-
可以对用户发送的内容进行过滤,让最终的内容发生变化或拦截
小结
- JavaWeb的三种组件
- Servlet
- Filter
- Listener
- 过滤器的使用场景:对请求和响应进行修改或拦截
2. 案例:编写第1个过滤器【重点】
目标
-
过滤器的开发步骤
-
编写第1个过滤器
过滤器的演示案例:
需求
- Web资源是:HelloServlet
- 过滤器:HelloFilter
创建一个过滤器HelloFilter,在运行HelloServlet前和后分别输出一句话,在HelloServlet中也输出一句话,观察控制台的运行效果。
执行效果:
Servlet的代码
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/demo1")
public class Demo1HelloServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("到达Web资源:Servlet");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
使用注解的方式
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 创建过滤器的步骤:
* 1. 创建一个类,实现javax.servlet.Filter接口 (注意不要选错)
* 2. 要重写接口中所有的方法,其中doFilter方法就是执行过滤功能的方法
* 3. 要在web.xml中配置过滤器的过滤地址或使用@WebFilter注解来配置
*/
@WebFilter("/demo1") //可以过滤所有的资源或过滤某些指定的资源,这是它过滤的地址,不是访问地址
public class Demo1HelloFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
/**
* 执行过滤的功能
* @param req 请求对象,它是HttpServletRequest的父接口,其实是同一个对象
* @param resp 响应对象,它是HttpServletResponse的父接口,其实是同一个对象
* @param chain 过滤器链
*/
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
System.out.println("过滤器:请求的时候执行");
//调用过滤器链的方法,如果执行这句话,请求被放行,到达web资源;如果没有执行,请求就被拦截
chain.doFilter(req,resp);
System.out.println("过滤器:响应的时候执行");
}
@Override
public void destroy() {
}
}
使用配置文件的方式
<?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">
<!--使用配置的方式指定过滤器-->
<filter>
<!--过滤器的名字-->
<filter-name>demo1</filter-name>
<!--过滤器的类全名-->
<filter-class>com.itheima.filter.Demo1HelloFilter</filter-class>
</filter>
<!--指定过滤器的过滤地址-->
<filter-mapping>
<!--与上面的名字相同-->
<filter-name>demo1</filter-name>
<!--这是过滤地址,不是访问地址,必须以/开头-->
<url-pattern>/demo1</url-pattern>
</filter-mapping>
</web-app>
小结
-
过滤器的编写要实现哪个接口?
javax.servlet.Filter
-
过滤的方法是哪个?
doFilter(请求,响应,过滤链)
-
@WebFilter注解
@WebFilter注解属性 | 说明 |
---|---|
filterName | 过滤器的名字 |
urlPatterns | 过滤的地址:是一个字符串的数组,可以指定多个 |
value | 同上 |
3. 过滤器的执行流程
目标
过滤器的执行流程
过滤器的执行流程
过滤器也是运行在Web容器中
过滤器的执行流程如下:
- 用户发送请求,请求的是Web资源。
- 如果请求的资源访问地址与过滤器过滤的地址匹配,就会执行过滤器
- 执行过滤器中doFilter方法
- 在doFilter中再调用一个chain.doFilter()方法放行,如果没有执行这句话就是拦截
- 如果放行,就会到达web资源
- 响应回来的时候还会再次经过过滤器,并且执行doFilter()放行后面的代码
- 返回到浏览器端
4. 过滤器的生命周期【重点】
目标
过滤器的生命周期有哪些方法
过滤器加载的时机:
-
回顾:以前Servlet是什么时间加载的?
用户第一次访问的时候加载
-
Filter什么时候加载呢?
因为过滤器要拦截其它的资源,所以必须比其它资源更早实例化。在服务器启动的时候就加载了。
生命周期的方法
Filter接口中的方法 | 作用和执行次数 |
---|---|
void init(FilterConfig filterConfig) | 初始化的时候执行,执行1次 |
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | 只要匹配过滤的地址,每次请求都会执行,执行多次 |
public void destroy() | 服务器关闭的时候,销毁执行1次 |
示例:生命周期的过程
执行效果
案例代码
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.sql.Timestamp;
/*
filterName:表示过滤器的名字,不要出现同名
urlPatterns:过滤的地址
*/
@WebFilter(filterName = "Demo2LifeCycleFilter", urlPatterns = "/demo1")
public class Demo2LifeCycleFilter implements Filter {
//初始化的方法,在tomcat启动的时候,执行1次
public void init(FilterConfig config) throws ServletException {
System.out.println(new Timestamp(System.currentTimeMillis()) + " 初始化过滤器");
}
//每次请求都会执行
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println(new Timestamp(System.currentTimeMillis()) + "过滤器:请求的时候执行");
//放行
chain.doFilter(req, resp);
System.out.println(new Timestamp(System.currentTimeMillis()) + "过滤器:响应的时候执行");
}
//销毁的时候执行1次
public void destroy() {
System.out.println(new Timestamp(System.currentTimeMillis()) + "过滤器销毁");
}
}
小结
- 过滤器什么时候执行初始化?服务器启动
- 过滤的方法执行多少次?多次
- 过滤器什么时候销毁?服务器关闭的时候
5. FilterConfig接口
目标
学习FilterConfig接口的方法
方法
使用配置文件的方式中
FilterConfig接口中的方法 | 功能 |
---|---|
String getInitParameter(“参数名”) | 通过参数名获取配置文件中初始的参数值 |
Enumeration<String> getInitParameterNames() | 获取配置文件中所有的初始参数名 |
代码
过滤器代码
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.util.Enumeration;
/**
* 读取配置参数,所以使用配置的方式
*/
public class Demo3ConfigFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//放行
chain.doFilter(req, resp);
}
//过滤器的配置对象,通过init方法传递进来,可以在方法中直接使用
public void init(FilterConfig config) throws ServletException {
//通过参数名读取一个初始的参数值
String user = config.getInitParameter("user");
System.out.println("参数:" + user);
//读取所有的初始参数名字
Enumeration<String> parameterNames = config.getInitParameterNames();
//遍历
while (parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement(); //获取名字
String value = config.getInitParameter(name); //通过名字获取值
System.out.println("初始参数名:" + name + ",值:" + value);
}
}
}
web.xml
<filter>
<filter-name>demo3</filter-name>
<filter-class>com.itheima.filter.Demo3ConfigFilter</filter-class>
<!--添加初始的参数-->
<init-param>
<param-name>user</param-name>
<param-value>Rose</param-value>
</init-param>
<init-param>
<param-name>age</param-name>
<param-value>20</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>demo3</filter-name>
<url-pattern>/demo1</url-pattern>
</filter-mapping>
效果
只要启动服务器就被初始化,就执行了init()方法中代码
参数:Rose
初始参数名:user,值:Rose
初始参数名:age,值:20
小结
FilterConfig接口中有以下两个方法:
getInitParameter() 通过初始的参数名,获取参数值
getInitParameterNames() 获取所有的初始参数名字
6. 过滤器映射的访问路径
目标
理解过滤器的映射路径的写法
Servlet中与过滤器中映射路径的区别
-
Servlet:路径就是它的访问地址
@WebServlet("/demo1")
-
Filter:不是它的访问地址,是它的过滤地址
@WebFilter("/demo1")
-
疑问:浏览器访问目标资源的路径,如果目标地址不存在,过滤器会不会运行?
只要匹配过滤地址,无论目标资源是否存在,都会执行过滤器
过滤地址编写方式
匹配方式 | 匹配哪些资源 | 示例 |
---|---|---|
以/开头 | 精确匹配,访问的资源地址与过滤的地址完全一样 | /demo1 |
目录匹配,过滤的是某个目录下所有的资源 | /admin/* | |
过滤所有的资源,整个web模块下所有的资源都会过滤 | /* | |
以扩展名结尾 | 匹配指定的扩展名就会被过滤 | *.do |
-
疑问:以/开头的匹配模式和以扩展名结尾的配置,同时出现会怎样?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pJnxNvJP-1599131780590)(assets/image-20200903101959187.png)]
Caused by: java.lang.IllegalArgumentException: 过滤器映射中的<url-pattern> [/*.do] 无效 结论:不能同时出现/开头和扩展名结尾,否则会导致tomcat加载这个模块失败,所有的资源都不能正常访问
过滤多个地址的写法
因为它的过滤地址是一个字符串的数组,可以指定多个过滤的地址
过滤器匹配多个地址 | 说明 |
---|---|
@WebFilter({"/demo1","/demo2"}) | 同时过滤demo1和demo2 |
过滤admin目录下所有的资源和所有的JSP页面 过滤的地址是所有过滤地址的并集,不是交集 |
小结:根据过滤路径判断指定的过滤器是否起作用
浏览器的访问地址 | 过滤器的配置 | 是否起作用 |
---|---|---|
http://localhost:8080/项目地址/aaa | /* | 是 |
http://localhost:8080/项目地址/aaa | /aaa | 是,精确匹配 |
http://localhost:8080/项目地址/aaa.do | *.do | 是,扩展名匹配 |
http://localhost:8080/项目地址/aaa/bbb | /aaa/* | 是,目录匹配 |
http://localhost:8080/项目地址/bbb.do | /*.do | 否,错误 |
http://localhost:8080/项目地址/aaa/bbb.action | /aaa/*.action | 否,错误 |
7. 过滤器的三种拦截方式
目标
过滤器常用的两种拦截方式
默认的拦截方式
在默认的情况下只有直接在地址栏上输入的访问地址,才会经过过滤器。这种拦截方式称为REQUEST拦截
案例
1). 在index.jsp转发到HelloServlet
2). 过滤器的配置
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<%-- 转发 --%>
<%--<jsp:forward page="/demo1"></jsp:forward>--%>
<%--包含页面--%>
<jsp:include page="/demo1"/>
</body>
</html>
配置方式1:注解的方式:
/*
filterName:表示过滤器的名字,不要出现同名
urlPatterns:过滤的地址
dispatcherTypes: 指定拦截方式
DispatcherType.FORWARD 拦截转发
DispatcherType.REQUEST 拦截直接在浏览器上输入的请求
DispatcherType.INCLUDE 拦截包含的页面
*/
@WebFilter(filterName = "Demo2LifeCycleFilter", urlPatterns = "/demo1",
dispatcherTypes = {DispatcherType.FORWARD, DispatcherType.REQUEST, DispatcherType.INCLUDE})
配置方式2:web.xml文件
<!--使用配置的方式指定过滤器-->
<filter>
<!--过滤器的名字-->
<filter-name>demo1</filter-name>
<!--过滤器的类全名-->
<filter-class>com.itheima.filter.Demo1HelloFilter</filter-class>
</filter>
<!--指定过滤器的过滤地址-->
<filter-mapping>
<!--与上面的名字相同-->
<filter-name>demo1</filter-name>
<!--这是过滤地址,不是访问地址,必须以/开头-->
<url-pattern>/demo1</url-pattern>
<!--拦截方式-->
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
小结:过滤器的拦截类型
过滤类型 | 作用 |
---|---|
REQUEST | 对正常的请求进行拦截 |
FORWARD | 对转发进行拦截 |
INCLUDE | 对包含进行拦截 |
8. 案例:使用过滤器过滤全局汉字乱码问题【重点】
目标
编写过滤器,过滤所有Servlet中使用POST方法提交的汉字的编码。
分析
开发步骤
- 有2个Servlet,一个是LoginServlet登录,一个是RegisterServlet注册
- 有2个JSP页面,1个是login.jsp,有表单,登录名。1个register.jsp,有表单,有注册的名字。都使用POST提交用户名使用汉字提交。
- 使用过滤器,对所有的Servlet的POST方法进行过滤。
- 在没有使用过滤器之前,每个Servlet必须加上汉字编码:request.setCharacterEncoding(字符集); 字符集与网页的编码要一致
代码
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h2>登录页面</h2>
<form action="login" method="post">
登录名:<input type="text" name="user"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户注册</title>
</head>
<body>
<h2>用户注册</h2>
<form action="register" method="post">
注册名:<input type="text" name="name"><br>
<input type="submit" value="注册">
</form>
</body>
</html>
LoginServlet.java
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//获取提交的用户名
String user = request.getParameter("user");
out.print("登录名是:" + user);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
RegisterServlet.java
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//获取参数
String name = request.getParameter("name");
out.print("注册的名字:" + name);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
EncodeFilter.java
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 设置为过滤所有的Servlet
*/
@WebFilter(filterName = "CharacterEncodingFilter", urlPatterns = "/*")
public class CharacterEncodingFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//1.判断是否是POST方法
HttpServletRequest request = (HttpServletRequest) req;
//2.这里POST是大写
if (request.getMethod().equals("POST")) {
//3.对POST方法进行编码
request.setCharacterEncoding("utf-8");
}
//4.要放行
chain.doFilter(req, resp);
}
public void init(FilterConfig config) throws ServletException {
}
}
小结
编写一个过滤器就可以对所有的Servlet进行汉字编码
- 使用哪个方法编码:request.setCharacterEncoding(“utf-8”)
- 过滤的地址: /*
- 放行使用哪个方法:chain.doFilter(请求,响应)
9. 案例:用户权限的过滤器【重点】
目标:
使用过滤器进行权限的控制,实现正确的访问
- add.jsp 添加数据,需要登录才可访问
- update.jsp 修改数据,需要登录才可访问
- list.jsp 查询数据,不用登录
- login.jsp 登录页面
项目结构
流程
实现步骤:
- 在Web下创建4个页面 login.jsp上使用${msg},显示信息。
- 创建LoginServlet, 判断用户名密码是否正确,如果正确,则在会话域中保存用户信息。登录成功跳转到add.jsp,登录失败则在域中写入登录失败的信息,并且跳转到login.jsp。
- 使用过滤器解决:创建AuthorityFilter
- 得到HttpServletRequest、HttpSession对象
- 如果会话中没有用户信息,则转到登录页面,并return。
- 否则继续访问后续的Web资源
案例代码:
登录页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录页面</title>
</head>
<body>
<h2>用户登录</h2>
<form action="login" method="post">
用户名:<input type="text" name="username"> <span id="info">${msg}</span><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
LoginServlet
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
//2.判断用户名和密码是否正确
if ("admin".equals(username) && "123".equals(password)) {
//3.如果正确,就把用户的信息放在会话域中
HttpSession session = request.getSession();
session.setAttribute("username", username);
//4.重定向到add.jsp (添加页面)
response.sendRedirect(request.getContextPath() + "/admin/add.jsp");
}
//5.登录失败,向请求域中添加信息,转发到JSP页面上显示
else {
request.setAttribute("msg", "用户名或密码错误");
//这里必须使用转发,因为要保留请求域中数据
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
AuthorityFilter
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 注:这个过滤地址不能指定为/*所有,必须要修改为/admin/*
* 没有登录进行拦截,将用户重定向到登录页面
*/
@WebFilter(filterName = "AuthorityFilter", urlPatterns = "/admin/*")
public class AuthorityFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//1. 过滤器中判断会话域是否有用户的信息
//将父接口转成子接口
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//获取会话对象
HttpSession session = request.getSession();
String username = (String) session.getAttribute("username");
if (username != null) {
//2. 如果有表示已经登录,放行
chain.doFilter(req, resp);
}
else {
//3. 如果没有就不能访问,进行拦截
response.sendRedirect(request.getContextPath() + "/login.jsp");
}
}
public void init(FilterConfig config) throws ServletException {
}
}
小结
- 过滤的地址是:/admin/*
- 注:过滤器中使用的是请求对象和响应对象都是父接口,如果要使用子接口中方法,必须进行强转
10. 过滤器链FilterChain的使用
目标
-
什么是过滤器链
-
过滤器链的执行顺序是怎样的
过滤器链的概念
浏览器端请求Web资源,如果经过了多个过滤器,这多个过滤器就组成了一个过滤器链。
链条中每个过滤器处理方式是一样的:如果下一个是过滤器就会把请求放行给过滤器,如果下一个是Web资源,当前就是最后一个过滤器,请求就交给Web资源。
组成过滤器链前提是:这些过滤器的过滤地址都能过滤同一个Web资源。
过滤器链有一个接口:FilterChain
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X9LvtSEi-1599131780597)(assets/1552919196802.png)]
FilterChain接口中的方法
void doFilter(ServletRequest request, ServletResponse response)
参数1:请求对象 参数2:响应对象
将请求和响应向后传递,导致调用链中的下一个过滤器,或者如果调用过滤器是链中的最后一个过滤器,则导致调用链末端的Web资源。
示例:过滤器链 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qOw3LQ4Z-1599131780655)(assets/1552919236666.png)]
需求
创建两个过滤器OneFilter和TwoFilter,访问ResourceServlet,每个过滤器的请求和响应各输出一句话,观察过滤器的执行过程。
执行效果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J6xQu2Ct-1599131780658)(assets/1552919267302.png)]
第一个过滤器
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.sql.Time;
@WebFilter(filterName = "OneFilter", urlPatterns = "/*")
public class OneFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println(new Time(System.currentTimeMillis()) + " 执行过滤器1的请求");
chain.doFilter(req, resp); //过滤器链的方法,放行
System.out.println(new Time(System.currentTimeMillis()) + " 执行过滤器1的响应");
}
public void init(FilterConfig config) throws ServletException {
}
}
第二个过滤器
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.sql.Time;
@WebFilter(filterName = "TwoFilter", urlPatterns = "/*")
public class TwoFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println(new Time(System.currentTimeMillis()) + " 执行过滤器2的请求");
chain.doFilter(req, resp);
System.out.println(new Time(System.currentTimeMillis()) + " 执行过滤器2的响应");
}
public void init(FilterConfig config) throws ServletException {
}
}
Web资源
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Time;
/**
* 这是web资源
*/
@WebServlet("/resource")
public class ResourceServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(new Time(System.currentTimeMillis()) + " 访问Web资源");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
疑问:过滤器链的执行顺序是怎样的?
注:要么使用注解,要么使用配置的方式
- 使用web.xml的配置方式:
<?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">
<!--如果有多个过滤器,哪个过滤器配置在前面,哪个就先执行 -->
<filter>
<filter-name>one</filter-name>
<filter-class>com.itheima.filter.OneFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>one</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>two</filter-name>
<filter-class>com.itheima.filter.TwoFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>two</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
- 使用注解的配置方式:
按过滤器类名的字母顺序排序,哪个在前面,哪个就先执行
小结
过滤器链的执行顺序是?
- 配置:按出现在web.xml中先后顺序
- 注解:按过滤器类名的字母顺序排序,哪个在前面,哪个就先执行
11. 案例:过滤敏感词汇
需求
当用户发帖的时候,如果发出敏感词汇就进行过滤,并提示发贴失败,否则显示正常的发贴内容。
案例效果
- 在表单中输入含有非法字符的言论,点击提交按钮
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMHgMS3F-1599131780663)(assets/Snipaste_2020-03-27_11-00-41.png)]
- 控制台显示如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lW9anHB7-1599131780666)(assets/Snipaste_2020-03-27_11-00-32.png)]
- 正常发贴的情况
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-94Ar7qTZ-1599131780675)(assets/Snipaste_2020-03-27_11-00-58.png)]
案例分析
- 创建一个表单用于发表言论。
- 创建一个PostWordServlet,正常接收用户的输入信息,并且打印到浏览器
- 创建一个words.txt文件,其中存入非法字符。
- 创建一个Filter,只过滤PostWordServlet。
- 在init方法中将txt文件中的非法字符读取到List集合中。注:指定字符的编码为utf-8
- 在doFilter方法中,获取请求中的参数,遍历上面的List集合,判断请求的文字中是否包含非法字符。
- 如果言论中含有非法字符,就拦截,并且直接在过滤器中打印提示:非法言论,退出过滤器。
- 否则就放行
实现步骤
- 创建一个表单,用于发表言论
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>发贴</title>
</head>
<body>
<form method="post" action="PostWordServlet">
发贴:<br>
<textarea name="message" rows="5" cols="40"></textarea></br> <hr/>
<input type="submit" value="提交">
</form>
</body>
</html>
- 创建一个txt文件,存入非法字符。要注意,文件存储使用的UTF-8字符集,否则可能出现乱码。
建议直接使用提供的word.txt文件,放在src目录下。
穷逼
笨蛋
白痴
王八
贱人
傻逼
- 创建一个servlet用于接受表单提交的内容
package com.itheima.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/PostWordServlet")
public class PostWordServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("<h2>你发贴的内容如下:</h2>");
//获取文本域的内容
String message = request.getParameter("message");
out.print(message);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
}
- 创建一个过滤器,用来拦截请求,过滤请求中发表的言论的非法字符
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* 要过滤Servlet
*/
@WebFilter(filterName = "WordFilter", urlPatterns = "/PostWordServlet")
public class WordFilter implements Filter {
//保存文件中非法字符
private List<String> words = new ArrayList<>();
//在初始化的方法中读取文本文件的内容,放在一个集合中
public void init(FilterConfig config) throws ServletException {
//1.获取文件words.txt的输入流,类加载器默认从根目录下读取资源,类对象默认是从当前类所在的包中读取资源
InputStream in = this.getClass().getResourceAsStream("/words.txt");
//2.使用转换流把字节流转成字符流
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, "utf-8"))) {
String line = null;
//3. 如果不为空,就继续向下读取一行
while ((line = reader.readLine()) != null) {
//每一行要添加到集合中
words.add(line);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("读取到的集合:" + words);
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//解决汉字乱码的问题
request.setCharacterEncoding("utf-8");
//获取用户提交的内容
String message = request.getParameter("message");
//遍历集合
for (String word : words) {
//判断文字中是否包含非法字符,如果包含就拦截
if (message.contains(word)) {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.print("您的言论非法,发贴无效");
//退出方法,没有放行,不会进入到Servlet中去
return;
}
}
//不包含就放行
chain.doFilter(req, resp);
}
public void destroy() {
}
}
小结
过滤敏感词汇过滤器的开发步骤:
- 读取src下文本文件,放在一个集合中
- 在过滤的方法中去判断用户的输入是否包含指定的字符
- 如果包含就拦截,没有包含就放行
12. 扩展:使用装饰者模式进行改进
目标
使用装饰者模式改进上面的功能,只替换敏感词汇为*号,其它内容不变
回顾装饰者模式
什么是装饰者模式
装饰者模式是在不改变原类文件,使用继承的情况下,动态地扩展一个类的功能。
它是通过创建一个子类对象,也就是装饰对象来包裹真实的对象。
使用场景
在开发过程中,如果发现某个类的某个(某些)方法不满足需求(不够用),那么可以使用装饰者模式对该类
进行装饰,增强这个类的方法。
装饰者模式的作用:专门对类的方法进行增强!
装饰者模式中的各个角色
- 抽象角色(HttpServletRequest接口):给出一个抽象接口或父类,以规范准备接收附加功能的对象(即具体角色)。
- 具体角色(HttpServletRequestWrapper实现类):定义一个将要接收附加功能的类。
- 装饰角色(继承于HttpServletRequestWrapper):持有一个具体角色的实例,继承于抽象角色或具体角色,给具体角色添加附加功能的类。
步骤
- 继承于被装饰的类(要进行功能增强类HttpServletRequestWrapper)
- 需要在构造方法中传入原来的对象
- 重写需要增强的方法:getParameter()
- 重写的方法中调用原来的方法,对结果进行增强
- 调用原来的方法,包含有脏话的内容
- 对脏话的集合进行遍历
- 如果信息中包含了某个元素
- 使用replaceAll进行替换
- 返回替换好的字符串
- 过滤器中需要放行增强后的请求对象
代码
package com.itheima.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* 要过滤Servlet
*/
@WebFilter(filterName = "WordFilter", urlPatterns = "/PostWordServlet")
public class WordFilter implements Filter {
//保存文件中非法字符
private List<String> words = new ArrayList<>();
//在初始化的方法中读取文本文件的内容,放在一个集合中
public void init(FilterConfig config) throws ServletException {
//1.获取文件words.txt的输入流,类加载器默认从根目录下读取资源,类对象默认是从当前类所在的包中读取资源
InputStream in = this.getClass().getResourceAsStream("/words.txt");
//2.使用转换流把字节流转成字符流
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, "utf-8"))) {
String line = null;
//3. 如果不为空,就继续向下读取一行
while ((line = reader.readLine()) != null) {
//每一行要添加到集合中
words.add(line);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("读取到的集合:" + words);
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//解决汉字乱码的问题
request.setCharacterEncoding("utf-8");
//创建一个增强以后的请求对象
WordRequestWrapper requestWrapper = new WordRequestWrapper(request);
//放行的是增强以后的请求对象
chain.doFilter(requestWrapper, resp);
}
public void destroy() {
}
/**
* 写成一个内部类,可以直接使用List集合
* HttpServletRequestWrapper类没有无参的构造方法
*/
class WordRequestWrapper extends HttpServletRequestWrapper {
//请求对象,没有增强前的对象
private HttpServletRequest request;
//构造方法,接收外面传递进来的请求对象,这个请求对象就是我们要增强的对象
public WordRequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}
//重写父类的这个方法,对这个方法进行增强
@Override
public String getParameter(String name) {
//调用原来的方法,获取用户发贴的内容
String message = request.getParameter(name);
//遍历非法内容的集合
for (String word : words) {
//判断内容是否包含脏话
if (message.contains(word)) {
//将文本中的脏话换成*号
message = message.replaceAll(word, "**");
}
}
//到这里message就是已经替换完成了
return message;
}
}
}
小结
装饰者模式:对现在的类方法的功能进行增强,不用修改原有的类
13. 监听器的概述
目标
-
监听器的作用
-
常用的监听器有哪些
作用
- 监听作用域的创建和销毁
- 监听作用域中属性的变化
回顾:三种作用域的创建与销毁时机
作用域 | 接口名 | 作用范围 | 生命周期 |
---|---|---|---|
请求域 | HttpServletRequest | 一个用户的一次请求 | 一次请求结束 |
会话域 | HttpSession | 一个用户的所有请求 | 会话过期 |
上下文域 | ServletContext | 所有用户的所有请求 | 服务器关闭的时候 |
监听器接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cUElCnqy-1599131780678)(assets/1552919771117.png)]
小结
- 上下文域创建和销毁的监听接口:ServletContextListener
- 上下文域属性修改的监听接口:ServletContextAttributeListener
14. ServletContextListener监听器【重点】
目标
-
ServletContextListener接口有哪些方法
-
编写ServletContextListener监听器
ServletContextListener监听器的概述
- 作用:监听上下文域的创建和销毁
- 创建时机: 服务器启动并且加载当前项目的时候
- 销毁时机: 服务器关闭的时候
案例:ServletContextListener的应用
需求:
在Web项目加载和结束的时候在控制台各打印输出现在的时间戳,并且输出一句话
代码
监听器
package com.itheima.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebListener;
import java.sql.Timestamp;
/**
* 监听器类似于JS中事件
* 创建监听器的步骤
* 1. 创建一个类实现ServletContextListener接口
* 2. 重写接口中方法,包含两个方法,分别对应创建和销毁
* 3. 在web.xml中配置监听器或使用注解@WebListener
*/
//@WebListener
public class MyContextListener implements ServletContextListener {
//监听上下文对象的创建
@Override
public void contextInitialized(ServletContextEvent event) {
System.out.println(event.getServletContext() + "创建了上下文对象");
System.out.println(new Timestamp(System.currentTimeMillis()) + "上下文对象创建");
}
//监听上下文对象的销毁
@Override
public void contextDestroyed(ServletContextEvent event) {
System.out.println(new Timestamp(System.currentTimeMillis()) + "上下文对象销毁");
}
}
web.xml
<?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">
<!--配置监听器-->
<listener>
<listener-class>com.itheima.listener.MyContextListener</listener-class>
</listener>
</web-app>
执行效果
org.apache.catalina.core.ApplicationContextFacade@d7685c6创建了上下文对象
2020-09-03 16:10:09.16上下文对象创建
2020-09-03 16:10:25.959上下文对象销毁
小结
ServletContextListener接口中的方法
接口中的方法 | 功能 | 执行次数 |
---|---|---|
void contextDestroyed(ServletContextEvent sce) | 监听上下文对象的销毁 | 1次 |
void contextInitialized(ServletContextEvent sce) | 监听上下文对象的创建 | 1次 |
ServletContextEvent事件对象的方法
ServletContextEvent中的方法 | 功能 |
---|---|
ServletContext getServletContext() | 获取创建的上下文对象 |
15. ServletContextAttributeListener监听器
目标
-
ServletContextAttributeListener监听器触发的时机
-
ServletContextAttributeListener接口方法中的方法
ServletContextAttributeListener监听器的作用
-
作用:监听上下文域中属性的增删改操作
-
时机:
- 增加属性:setAttribute()
- 删除属性:removeAttribute()
- 修改属性:setAttribute(同名)
ServletContextAttributeListener监听器的示例
案例需求
-
创建一个ServletContextAttributeListener监听器的实现类,重写接口中所有的方法,输出属性名和属性值
-
创建一个Servlet,向context上下文中添加一个属性,修改一个属性,删除一个属性。
- 注:修改后的属性值,要通过上下文对象来取得。
案例效果
向上下文域中添加了属性名:user,值:孙悟空
修改了上下文域中属性名:user,修改前的值:孙悟空,修改后的值:白骨精
删除了上下文域中属性名:user,值:白骨精
案例代码
package com.itheima.listener;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebListener;
import java.sql.Timestamp;
/**
* 监听器类似于JS中事件
* 创建监听器的步骤
* 1. 创建一个类实现ServletContextListener接口
* 2. 重写接口中方法,包含两个方法,分别对应创建和销毁
* 3. 在web.xml中配置监听器或使用注解@WebListener
*/
//@WebListener
public class MyContextListener implements ServletContextListener, ServletContextAttributeListener {
//监听上下文对象的创建
@Override
public void contextInitialized(ServletContextEvent event) {
System.out.println(event.getServletContext() + "创建了上下文对象");
System.out.println(new Timestamp(System.currentTimeMillis()) + "上下文对象创建");
}
//监听上下文对象的销毁
@Override
public void contextDestroyed(ServletContextEvent event) {
System.out.println(new Timestamp(System.currentTimeMillis()) + "上下文对象销毁");
}
//监听上下文域属性添加
@Override
public void attributeAdded(ServletContextAttributeEvent event) {
//getName()获取属性名, getValue()获取属性值
System.out.println("向上下文域中添加了属性名:" + event.getName() + ",值:" + event.getValue());
}
//监听上下文域属性删除
@Override
public void attributeRemoved(ServletContextAttributeEvent event) {
System.out.println("删除了上下文域中属性名:" + event.getName() + ",值:" + event.getValue());
}
//监听上下文域属性修改
@Override
public void attributeReplaced(ServletContextAttributeEvent event) {
//获取上下文对象
ServletContext application = event.getServletContext();
//通过名字获取值,获取修改后的
Object value = application.getAttribute(event.getName());
System.out.println("修改了上下文域中属性名:" + event.getName() + ",修改前的值:" + event.getValue() + ",修改后的值:" + value);
}
}
小结
ServletContextAttributeListener接口中的方法
接口中的方法 | 功能 |
---|---|
void attributeAdded(ServletContextAttributeEvent event) | 监听上下文域的添加事件 |
void attributeRemoved(ServletContextAttributeEvent event) | 监听上下文域的删除事件 |
void attributeReplaced(ServletContextAttributeEvent event) | 监听上下文域的修改事件 |
ServletContextAttributeEvent对象中的方法
ServletContextAttributeEvent对象中的方法 | 功能 |
---|---|
String getName() | 获取属性名 |
Object getValue() | 获取属性值 |
16. 案例:统计网站当前在线人数
执行效果
页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ILwkvo3-1599131780680)(assets/image-20200903165954858.png)]
服务器控制台信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2EJypeDJ-1599131780682)(assets/Snipaste_2020-03-27_16-30-54.png)]
分析
每当一个用户访问项目的时候,都会创建一个session会话。所以当session会话被创建,当前在线用户+1,每当session会话被销毁,当前在线用户-1。
HttpSessionListener可以用来监听session对象的创建和销毁的。所以可以在HttpSessionListener中的监听session对象创建和销毁的方法中控制在线人数的加减。
步骤
-
创建一个监听器 SessionCountListener
-
创建一个成员变量AtomicInteger,用于计数。注:必须是同一个对象
a) 监听会话创建的方法
i. 从上下文域中取出当前的计数对象
ii. 如果为空,表示是第1个用户,设置值为1,并且添加到上下文域中
iii. 不为空则加1,不用更新上下文域
b) 监听会话销毁的方法
i. 从上下文域中得到当前在线的人数
ii. 减1即可
-
创建一个注销的LogoutServlet
a) 让会话失效
b) 打印输出:您已经安全退出网站
-
编写JSP
a) 在JSP上取出上下文域中用户数显示
b) 显示安全退出的链接
代码
监听器
package com.itheima.listener;
import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 创建一个监听器,监听会话的创建和销毁
*/
@WebListener
public class SessionCountListener implements HttpSessionListener {
//创建一个成员变量计数,考虑线程安全问题
private AtomicInteger number;
//监听会话的创建
@Override
public void sessionCreated(HttpSessionEvent event) {
//获取会话对象
HttpSession session = event.getSession();
//通过会话对象获取上下文对象
ServletContext application = session.getServletContext();
//从上下文域中获取number的值
number = (AtomicInteger) application.getAttribute("number");
//第1个用户访问的时候number是为空
if (number == null) {
//创建一个1的值
number = new AtomicInteger(1);
application.setAttribute("number", number);
}
else {
//不为空,加1
number.incrementAndGet();
//因为AtomicInteger这是一个引用类型,获取的是它的地址,不需要进行更新
}
//在控制台输出
System.out.println("创建会话:" + session.getId() + ",当前在线人数:" + number);
}
//监听会话的销毁
@Override
public void sessionDestroyed(HttpSessionEvent event) {
//获取会话对象
HttpSession session = event.getSession();
//通过会话对象获取上下文对象
ServletContext application = session.getServletContext();
//从上下文域中获取number的值
number = (AtomicInteger) application.getAttribute("number");
//减1
number.decrementAndGet();
//在控制台输出
System.out.println("销毁会话:" + session.getId() + ",当前在线人数:" + number);
}
}
退出
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>退出</title>
</head>
<body>
退出成功
<%
session.invalidate();
%>
</body>
</html>
显示人数的JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>显示在线人数</title>
</head>
<body>
<%--所有的用户都可以访问,这个计数的值放在上下文域中--%>
<h3>当前在线人数是:${applicationScope.number}</h3>
<a href="logout.jsp">退出</a>
</body>
</html>
小结
-
HttpSessionListener接口的作用是什么?
监听会话的创建和销毁
-
说说以下方法的作用:
HttpSessionListener接口中的方法 | 作用 |
---|---|
void sessionCreated(HttpSessionEvent event) | 监听会话的创建 |
void sessionDestroyed(HttpSessionEvent event) | 监听会话的销毁 |
学习总结
-
能够说出过滤器的作用
- 修改请求
- 拦截请求
应用场景:
- 全局乱码问题
- 用户权限拦截
- 过滤敏感词汇
-
能够编写过滤器
- 创建一个类实现javax.servlet.Filter接口
- 重写所有的方法,其中doFilter方法是执行过滤的方法
- 使用web.xml配置或@WebFilter("/过滤地址")注解进行配置
-
能够说出过滤器生命周期相关方法
Filter接口中的方法 作用和执行次数 void init(FilterConfig filterConfig) 初始化的时候执行1次,服务器启动的时候 void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain)每次请求都会执行 public void destroy() 服务器关闭的时候执行,执行1次 -
能够根据过滤路径判断指定的过滤器是否起作用
匹配方式 匹配哪些资源 以/开头 精确匹配:/demo1 目录匹配: /目录/* 匹配所有的资源:/* 以扩展名结尾 *.扩展名
注:/开头和扩展名结尾不能同时出现 -
能够说出什么是过滤器链
执行顺序:
- xml配置方式:哪个配置在前面就先执行哪个
- 注解的方式:按过滤器类名的字母先后顺序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZARWRwTu-1599131780683)(assets/1552919236666.png)]
-
能够编写过滤器解决全局乱码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RlMeEY3T-1599131780685)(assets/1552918911663.png)]
-
能够说出监听器的作用
- 监听作用域的创建和销毁
- 监听作用域的属性变化
-
能够使用ServletContextListener监听器
- 创建一个类实现ServletContextListener接口
- 重写监听创建和销毁的方法
- 在web.xml中配置或使用@WebListener注解