Filter过滤器
概念
- Filter表示过滤器,是JavaWeb的三大组件之一,Servlet、Filter、Listener。Filter和Listener都是辅助Servlet的组件。
作用
- 过滤器可以将资源的请求进行拦截,实现一些特殊功能的通用操作
- 统一编码处理(POST请求中文乱码,字符集统一设置为UTF-8)
- VIP功能(只能允许VIP用户访问)
- 敏感字处理(敏感字替换为*等)
Filter快速入门
image-20221102203804502.png-
注解方式
- 定义
Demo1Servlet
类,继承于Servlet
- 类头添加
@WebServlet
注解,并设置value值为/demo1
- 在doGet和doPost方法中,向控制台打印一句话
- 定义
@WebServlet(value = "/demo1")
public class Demo1Servlet extends HttpServlet {
private static final long serialVersionUID = -4692985281158768196L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("访问web资源 demo1");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 新建
Demo1Filter
类,实现Filter
接口 - 类头添加
@WebFilter
注解,并设置value值为demo1(需要拦截的资源路径) - 复写
doFilter
方法,chain.doFilter代表放行(不拦截),在放行前后打印一句话 - 访问该项目的demo1路径的Servlet
@WebFilter(value = "/demo1")
public class Demo1Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Demo1Filter 过滤前逻辑...");
chain.doFilter(request, response);
System.out.println("Demo1Filter 过滤后逻辑...");
}
@Override
public void destroy() {
}
}
-
XML方式
- 类头没有注解,其他部分和注解方式一致
public class Demo3Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Demo3Filter 过滤前逻辑...");
chain.doFilter(request, response);
System.out.println("Demo3Filter 过滤后逻辑...");
}
@Override
public void destroy() {
}
}
- web.xml中配置Filter
- filter标签,配置过滤器的名称
- filter-mapping标签,配置拦截路径
<?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">
<!-- XML方式配置Filter过滤器 -->
<!-- 配置过滤器的名称 -->
<filter>
<filter-name>demo3_filter</filter-name>
<filter-class>com.itheima.filter.Demo3Filter</filter-class>
</filter>
<!-- 配置拦截路径 -->
<filter-mapping>
<filter-name>demo3_filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
- 结果
Demo1Filter 过滤前逻辑...
访问web资源 demo1
Demo1Filter 过滤后逻辑...
Filter执行流程
规律
- 访问web资源的时候,首先会经过过滤器执行
放行前代码
- 然后再访问web资源
- 最后再返回到过滤器
执行放行后代码
思考
-
放行后访问对应资源,资源访问完成后,还会回到Filter中吗?
- 会
-
如果回到Filter中,是重头执行还是执行放行后的逻辑呢?
- 放行后逻辑
-
如果过滤器代码如下,资源还会被访问吗?(不调用chain.doFilter)
- 不会,不调用
chain.doFilter
代表不放行(拦截)
- 不会,不调用
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Demo1Filter 过滤前逻辑...");
System.out.println("Demo1Filter 过滤后逻辑...");
}
Filter的拦截路径配置
-
/*
,表示任意匹配
,拦截所有的请求 -
/demo1
,表示精确匹配
,就只能拦截访问demo1路径的请求,其他资源不会被拦截 -
/user/*
,表示目录匹配
,只会拦截/user/目录下的所有请求 -
*.jsp
,表示后缀匹配
,只会拦截后缀为.jsp的请求
//任意匹配
@WebFilter(value = "/*")
//精确匹配
//@WebFilter(value = "/demo1")
//目录匹配
//@WebFilter(value = "/user/*")
//后缀名拦截
//@WebFilter(value = "*.jsp")
public class Demo1Filter implements Filter {
//...
}
Filter过滤器的优先级
- 注解方式配置,按照拦截器的名称进行自然排序
- XML方式配置,按照XML配置的先后顺序
Filter过滤器链
image-20221102204934232.png- 再定义一个Demo2Filter类,并将Demo1Filter和Demo2Filter的路径匹配都改为
/*
,表示拦截所有
@WebFilter(value = "/*")
public class Demo1Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Demo1Filter 过滤前逻辑...");
chain.doFilter(request, response);
System.out.println("Demo1Filter 过滤后逻辑...");
}
@Override
public void destroy() {
}
}
@WebFilter(value = "/*")
public class Demo2Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Demo2Filter 过滤前逻辑...");
chain.doFilter(request, response);
System.out.println("Demo2Filter 过滤后逻辑...");
}
@Override
public void destroy() {
}
}
- 结果
Demo1Filter 过滤前逻辑...
Demo2Filter 过滤前逻辑...
访问web资源 demo1
Demo2Filter 过滤后逻辑...
Demo1Filter 过滤后逻辑...
Filter实现全局,POST请求中文乱码问题
如果每个Servlet处理前,都需要先设置一次编码,就会出现大量的重复代码,并且后续如果需要修改,就需要多出。而Filter过滤器就可以实现统一处理
image-20221102211814132.png image-20221102212044223.png@WebFilter(value = "/*")
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest req = (HttpServletRequest) request;
String method = req.getMethod();
//解决POST请求中文乱码问题
if (method.equalsIgnoreCase("POST")) {
request.setCharacterEncoding("UTF-8");
}
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
Filter实现登录权限拦截
image-20221102212826343.png- LoginServlet,处理登录
/**
* 登录
*/
@WebServlet(value = "/loginServlet")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = -2612492748416781981L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决POST中文乱码问题(已在EncodingFilter过滤器中统一处理)
//request.setCharacterEncoding("UTF-8");
//获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String remember = request.getParameter("remember");
//是否勾选了记住我
boolean isRemember = "1".equals(remember);
//调用业务层查询用户信息
UserService userService = new UserService();
User user = userService.login(username, password);
//判断用户信息是否为null
if (user != null) {
//保存到session域,数据进行共享,使其他登录过的页面,才可以获取到数据
HttpSession session = request.getSession();
session.setAttribute("username", user.getUsername());
//不使用request域存储数据,那么使用重定向
response.sendRedirect("brand.jsp");
} else {
//如果为null,则为登录失败,跳转到登录页面,提示错误信息
//只有这个页面进行错误提示,所以使用请求转发
request.setAttribute("loginMessage", "登录失败,用户名或密码错误");
request.getRequestDispatcher("login.jsp").forward(request, response);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- LoginFilter,处理登录权限
/**
* 登录过滤器,当访问需要登录后的页面,如果未登录,则拦截请求,跳转到登录页面
*/
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest req = (HttpServletRequest) request;
//不需要的权限的资源的请求地址(白名单)
String[] whiteUris = {"css", "imgs", "login.jsp", "register.jsp", "checkCodeServlet", "loginServlet", "registerServlet"};
//获取请求路径
String requestURI = req.getRequestURI();
System.out.println("正在访问的资源 = " + requestURI);
for (String uri : whiteUris) {
//请求的资源,不需要登录,那么放行
if (requestURI.contains(uri)) {
chain.doFilter(request, response);
return;
}
}
//获取Session
HttpSession session = req.getSession();
//如果登录过,获取不为null
Object username = session.getAttribute("username");
//未登录,跳转到登录页面
if (username == null) {
//设置错误提示
req.setAttribute("loginMessage", "请先登录");
req.getRequestDispatcher("login.jsp").forward(request, response);
} else {
//已登录,放行
chain.doFilter(request, response);
}
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
}
Listener监听器
概念
- Listener代表监听器,JavaWeb三大组件(Servlet、Filter、Listener)之一
作用
- Listener监听器可以监听Application、Session、Request这3个对象的创建、销毁,以及给其添加、修改、删除属性时,自动执行代码的组件
Listener监听器的分类
- JavaWeb中提供了8个监听器
ServletContextListener使用
ServletContextListener监听器,对ServletContext对象的创建、销毁进行监听
- Web应用启动时互调
contextInitialized
,销毁时调用contextDestroyed
- 注意:程序重启时,会先销毁,再重新创建
/**
* 程序上下文生命周期监听
* 注意:程序重启时,会先销毁,再重新创建
*/
@WebListener
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ContextLoaderListener 程序启动");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ContextLoaderListener 程序销毁");
}
}
HttpSessionListener使用
HttpSessionListener监听,对Session对象的创建、销毁进行监听
20221118154235.png需求
- 实现在线人数统计
问题
- 浏览器关闭后,Session并没有马上销毁,所以导致监听器的sessionDestroyed方法,并不会及时回调
原因
- Session有一个无操作多长时间后自动销毁的时间,默认为30分钟,可以将时间调小,例如为1分钟来进行测试
实现代码
- web.xml,配置Session的自动销毁时间,单位为分钟
<?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">
<!--
浏览器关闭后,Session并没有马上销毁,所以导致监听器的sessionDestroyed方法,并不会及时回调
Session有一个无操作多长时间后自动销毁的时间,默认为30分钟,可以将时间调小,例如为1分钟来进行测试
-->
<!-- session的最大存活时间,单位为分钟,默认为30分钟 -->
<session-config>
<session-timeout>1</session-timeout>
</session-config>
</web-app>
- OnlineCountListener,在线人数统计监听器,继承于HttpSessionListener
/**
* 在线人数统计监听器
*/
@WebListener
public class OnlineCountListener implements HttpSessionListener {
public static final String ONLINE_COUNT_KEY = "onlineCount";
@Override
public void sessionCreated(HttpSessionEvent event) {
ServletContext servletContext = event.getSession().getServletContext();
System.out.println("有一位用户上线了,session id = " + event.getSession().getId());
Integer onlineCount = (Integer) servletContext.getAttribute(ONLINE_COUNT_KEY);
//第一个人登录,获取到的是null,那么初始化数量为1
if (onlineCount == null) {
onlineCount = 1;
} else {
//后续人数都增加1
onlineCount = onlineCount + 1;
}
//更新到全局 ServletContext 中
servletContext.setAttribute(ONLINE_COUNT_KEY, onlineCount);
}
@Override
public void sessionDestroyed(HttpSessionEvent event) {
ServletContext servletContext = event.getSession().getServletContext();
System.out.println("有一位用户下线了,session id = " + event.getSession().getId());
Integer onlineCount = (Integer) servletContext.getAttribute(ONLINE_COUNT_KEY);
if (onlineCount == null) {
onlineCount = 0;
} else {
//有用户下线,那么数量减少1
onlineCount = onlineCount - 1;
}
//更新到全局 ServletContext 中
servletContext.setAttribute(ONLINE_COUNT_KEY, onlineCount);
}
}
- 05_online_count.jsp,JSP页面,输出在线人数
<%--
Created by IntelliJ IDEA.
User: If
Date: 2022/11/18
Time: 15:10
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>在线人数</title>
</head>
<body>
<h1>当前有<span><%=config.getServletContext().getAttribute("onlineCount")%></span>人在线</h1>
</body>
</html>
网友评论