Web技术JavaWeb

Filter&Listener

2022-11-18  本文已影响0人  h2coder

Filter过滤器

概念

image-20221102185648847.png

作用

Filter快速入门

image-20221102203804502.png
@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);
    }
}
@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() {
    }
}
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() {
    }
}
<?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执行流程

规律

思考

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    System.out.println("Demo1Filter 过滤前逻辑...");
    System.out.println("Demo1Filter 过滤后逻辑...");
}

Filter的拦截路径配置

//任意匹配
@WebFilter(value = "/*")
//精确匹配
//@WebFilter(value = "/demo1")
//目录匹配
//@WebFilter(value = "/user/*")
//后缀名拦截
//@WebFilter(value = "*.jsp")
public class Demo1Filter implements Filter {
    //...
}

Filter过滤器的优先级

Filter过滤器链

image-20221102204934232.png
@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
/**
 * 登录
 */
@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);
    }
}
/**
 * 登录过滤器,当访问需要登录后的页面,如果未登录,则拦截请求,跳转到登录页面
 */
@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监听器

概念

image-20221102214313741.png

作用

Listener监听器的分类

ServletContextListener使用

ServletContextListener监听器,对ServletContext对象的创建、销毁进行监听

/**
 * 程序上下文生命周期监听
 * 注意:程序重启时,会先销毁,再重新创建
 */
@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

需求

问题

原因

实现代码

<?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>
/**
 * 在线人数统计监听器
 */
@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);
    }
}
<%--
  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>
上一篇 下一篇

猜你喜欢

热点阅读