监听器与过滤器保姆级解析

简介: Filter 过滤器它是 JavaWeb 的三大组件之一。三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器。 Filter 过滤器它是 JavaEE 的规范。也就是接口,它的作用是:拦截请求,过滤响应。

1.JPG


一、过滤器概述


1.1、什么是过滤器


   Filter 过滤器它是 JavaWeb 的三大组件之一。三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器。


   Filter 过滤器它是 JavaEE 的规范。也就是接口,它的作用是:拦截请求,过滤响应。


1.2、过滤器的流程


2.JPG


1.3、过滤器的实现


public class AdminFilter implements Filter {
    /**
    * doFilter 方法,专门用于拦截请求。可以做权限检查
    */
    @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse  servletResponse, FilterChainfilterChain) throws IOException, ServletException {
      HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
      HttpSession session = httpServletRequest.getSession();
      Object user = session.getAttribute("user");
        // 如果等于 null,说明还没有登录
    if (user == null) {    servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
      return;
  }else{
          // 让程序继续往下访问用户的目标资源,放行
    filterChain.doFilter(servletRequest,servletResponse);
      }
    }
}
复制代码

 

  随后在web.xml中配置


<!--filter 标签用于配置一个 Filter 过滤器-->
<filter>
  <!--给 filter 起一个别名-->
  <filter-name>AdminFilter</filter-name>
  <!--配置 filter 的全类名-->
  <filter-class>com.atguigu.filter.AdminFilter</filter-class>
</filter>
<!--filter-mapping 配置 Filter 过滤器的拦截路径-->
<filter-mapping>
  <!--filter-name 表示当前的拦截路径给哪个 filter 使用-->
  <filter-name>AdminFilter</filter-name>
  <!--url-pattern 配置拦截路径
  / 表示请求地址为:http://ip:port/工程路径/
  映射到 IDEA 的 web 目录
  /admin/* 表示请求地址为:http://ip:port/工程路径/admin/*
  -->
  <url-pattern>/admin/*</url-pattern>
</filter-mapping>
复制代码


1.4、Filter 的生命周期


1.5、FilterConfig 类


   FilterConfig 类它是 Filter 过滤器的配置文件类。Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。


   FilterConfig 类的作用是获取 filter 过滤器的配置内容:


  1. 获取 Filter 的名称 filter-name 的内容。
  2. 获取在 Filter 中配置的 init-param 初始化参数
  3. 获取 ServletContext 对象。


1.6、过滤器链


3.JPG


1.7、Filter的拦截路径


   Filter过滤器的拦截有三种路径匹配:

  1. 精准匹配
  2. 目录匹配
  3. 后缀名匹配


1.7.1、精准匹配


<!-- 以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/login.jsp -->
<url-pattern>/login.jsp</url-pattern>
复制代码


1.7.2、目录匹配


<!-- 以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/* -->
<url-pattern>/admin/*</url-pattern>
复制代码


1.7.3、后缀名匹配


<!-- 以上配置的路径,表示请求地址必须以.html 结尾才会拦截到 -->
<url-pattern>*.html</url-pattern>
<!-- 以上配置的路径,表示请求地址必须以.do 结尾才会拦截到  -->
<url-pattern>*.do</url-pattern>
复制代码

 

 Filter 过滤器它只关心请求的地址是否匹配,不关心请求的资源是否存在!!!


二、字符编码过滤器


   之前编码处理的代码是直接写 Servlet 中的,那么它存在的问题是代码重复,无法复用,同时增加了维护的成本,我们想要的是一处设置,全局生效。     我们的方案是访问到 Servlet 之前对请求中的编码做处理(Filter)。


2.1、代码实现


   我们编写 CharacterEncodingFilter类,统一在过滤器的 doFilter 方法中设置好编码为 UTF-8。


public class CharacterEncodingFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
  }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse
  servletResponse, FilterChain filterChain) throws IOException, ServletException {
    // 设置请求编码
    servletRequest.setCharacterEncoding("UTF-8");
    // 发行
    filterChain.doFilter(servletRequest, servletResponse);
  }
    @Override
    public void destroy() {
  s}
}
复制代码

 

接着在 web.xml 配置 CharacterEncodingFilter这个过滤器。


<filter>
  <filter-name>characterEncodingFilter</filter-name>
  <filter-class>cn.wolfcode.web.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>characterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
复制代码


2.2、代码优化


   在过滤器中,我们将请求参数的编码设值为 UTF-8,此时编码是写死在 Java 代码中的,若后期需要修改,那么这个 UTF-8 就是硬编码,不可以灵活配置。


2.2.1、配置字符编码


   Filter 类似 Servlet 一样,也可以配置初始化参数。在 web.xml 配置如下:


<filter>
  <filter-name>characterEncodingFilter</filter-name>
  <filter-class>cn.wolfcode.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
  <param-name>encoding</param-name>
  <param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
  <filter-name>characterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
复制代码


2.2.2、修改 CharacterEncodingFilter


   在初始化方法里从 FilterConfig 对象中获取配置的 encoding 的初始化参数值。


public class CharacterEncodingFilter implements Filter {
  private String encoding;
    @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    this.encoding = filterConfig.getInitParameter("encoding");
  }
  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse
  servletResponse, FilterChain filterChain) throws IOException, ServletException {
    if(StringUtil.hasLength(encoding) {
    // 设置请求编码
    servletRequest.setCharacterEncoding(this.encoding);
  }
    // 放行
    filterChain.doFilter(servletRequest, servletResponse);
  }
  @Override
  public void destroy() {
  }
}
复制代码


三、登录校验过滤器实战


3.1、以前的代码


   在实际开发中,我们项目中会存在很多的资源都需要在登录之后才能访问,所以我们需要在请求到达这些资源之前先判断当前用户是否登录,如果没有应该跳转到登录页面。之前我们是在需要登录访问的的资源加上登录判断代码。


Object obj = req.getSession().getAttribute("USER_IN_SESSION");
  if(obj == null){
  resp.sendRedirect("/login.jsp");
  return;
复制代码

 

这段代码是可以满足需求,但是存在大量重复代码,在每个资源的代码中都会编写检查登录的代码,从增加维护成本。完全违背了我们之前使用过滤器的初衷(设置一次,所有 Servlet 共用)。



3.2、过滤器代码实现


3.2.1、编写 CheckLoginFilter


public class CheckLoginFilter implements Filter {
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
  }
  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse
  servletResponse, FilterChain filterChain) throws IOException, ServletException {
    // 把请求和响应转化为符合 HTTP 协议的对象
    HttpServletRequest req = ((HttpServletRequest) servletRequest);
    HttpServletResponse resp = ((HttpServletResponse) servletResponse);
    Object obj = req.getSession().getAttribute("USER_IN_SESSION");
    // 没有登录,重定向到登录页面
    if(obj == null) {
    resp.sendRedirect("/login.jsp");
    return;
  }
    // 登录过,放行访问
  filterChain.doFilter(req, resp);
}
  @Override
  public void destroy() {
  }
}
复制代码


3.2.2、配置 CheckLoginFilter


   我们写完了过滤器还需要在 web.xml 配置此过滤器。


<filter>
  <filter-name>checkLoginFilter</filter-name>
  <filter-class>cn.wolfcode.web.filter.CheckLoginFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>checkLoginFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping> 
复制代码


3.3、匿名访问资源的方法


   匿名访问资源指的是不需要登录也可以访问的资源,比如 /login.jsp,/login,/randomCode 和静态资源等等。若不排除,那么当浏览器中请求这些资源,也需要登录,这就会造成这些资源访问不到了。针对上面的情况,只需要指定 CheckLoginFilter 对哪些资源做登录校验处理或者不对哪些资源做登录校验处理。


3.3.1、方式一:


   我们可以指定 CheckLoginFilter 不对哪些资源做登录校验处理,只需修改 CheckLoginFilter,配置初始化参数,指定哪些资源不做登录校验。


<filter>
  <filter-name>checkLoginFilter</filter-name>
  <filter-class>cn.wolfcode.web.filter.CheckLoginFilter</filter-class>
  <init-param>
    <param-name>unCheckUri</param-name>
  <param-value>/login.jsp;/login;/randomCode</param-value>
</init-param>
</filter>
<filter-mapping>
  <filter-name>checkLoginFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
复制代码
public class CheckLoginFilter implements Filter {
    private List<String> unCheckUriList;
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.unCheckUriList =
            Arrays.asList(filterConfig.getInitParameter("unCheckUri").split(";"));
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse
        servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = ((HttpServletRequest) servletRequest);
        HttpServletResponse resp = ((HttpServletResponse) servletResponse);
        Object obj = req.getSession().getAttribute("USER_IN_SESSION");
    // 获取请求的资源路径
        String uri = req.getRequestURI();
    // 没有登录且访问的不是匿名资源,重定向到登录页面
        if(obj == null && !unCheckUriList.contains(uri)) {
            resp.sendRedirect("/login.jsp");
            return;
        }
    // 登录过,放行访问
        filterChain.doFilter(req, resp);
    }
    @Override
    public void destroy() {
    }
}
复制代码


3.3.2、方式二


   因为会发现,上面那样处理还是会导致静态资源不可以访问到,而实际开发中,我们需要排除的资源较多的话,尤其是像静态资源,会很麻烦。所以这次指定 CheckLoginFilter 对哪些资源做登录校验处理。将需要受检查的资源通通存放到 check 路径下。     首先先修改 CheckLoginFilter 的过滤路径。


<filter>
  <filter-name>checkLoginFilter</filter-name>
  <filter-class>cn.wolfcode.web.filter.CheckLoginFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>checkLoginFilter</filter-name>
  <!-- 只有在 check 路径下的资源访问会做登录的校验 -->
  <url-pattern>/check/*</url-pattern>
</filter-mapping>
复制代码

 

  接着再修改修改 CheckLoginFilter


public class CheckLoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse
        servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = ((HttpServletRequest) servletRequest);
        HttpServletResponse resp = ((HttpServletResponse) servletResponse);
        Object obj = req.getSession().getAttribute("USER_IN_SESSION");
    // 没有登录,重定向到登录页面
        if(obj == null) {
            resp.sendRedirect("/login.jsp");
            return;
        }
    // 登录过,放行访问
        filterChain.doFilter(req, resp);
    }
    @Override
    public void destroy() {
    }
}
复制代码


   最后只要修改需要做登录校验的资源,加上 /check 路径即可。


四、监听器(Listener)


   Listener 是 Java Web 组件之一,主要的作用是用于监听作用域对象的创建和销毁动作以及作用域属性值的改变动作。若用户触发了这些动作,那么会立即执行相应的的监听器的操作。


4.1、监听器的分类


   监听器监听的对象是作用域对象,作用域属性,他监听的动作:作用域对象的创建和销毁,作用域属值的增删改。 监听器按照作用域对象可以分为:


  1. ServletRequestListener
  2. HttpSessionListener
  3. ServletContextListener


按作用域属性分:

  1. ServletRequestAttributeListener
  2. HttpSessionAttributeListener
  3. ServletContextAttributeListener


4.2、开发监听器步骤


  1. 创建一个类,根据需求实现对应的接口。
  2. 实现其中的方法。
  3. 将监听器交给 Tomcat 管理。


4.3、实战


   我们的需求是做一个网站统计游客的数量。


4.3.1、编写 VisitorListener


import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class VisitorListener implements HttpSessionListener {
    private static long totalCount = 0;
    // Session 对象创建会执行下面的方法
    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        totalCount++;
        System.out.println("在线人数:" + totalCount);
    }
    // Session 对象销毁会执行下面的方法
    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        totalCount--;
    }
}
复制代码


4.3.2、配置 VisitorListener


   对于配置 VisitorListener,我们可以使用注解配置(直接在自己写监听器类上贴 @WebListener 即可),也可以是 XML 配置。


<listener>
  <listener-class>cn.wolfcode.web.listener.VisitorListener</listener-class>
</listener>
相关文章
|
3天前
|
数据采集 监控 数据可视化
日志解析神器——Logstash中的Grok过滤器使用详解
日志解析神器——Logstash中的Grok过滤器使用详解
34 4
|
JSON JavaScript 前端开发
Gson与Fastjson两种Json解析神器保姆级使用攻略
Gson与Fastjson两种Json解析神器保姆级使用攻略
407 0
Gson与Fastjson两种Json解析神器保姆级使用攻略
|
JSON JavaScript 前端开发
JavaScript入门保姆级教程 ——— 重难点详细解析(万字长文,建议收藏)
基本语法、事件 🍅 Java学习路线:搬砖工的Java学习路线 🍅 作者:程序员小王 🍅 程序员小王的博客:https://www.wolai.com/wnaghengjie/ahNwvAUPG2Hb1Sy7Z8waaF 🍅 扫描主页左侧二维码,加我微信 一起学习、一起进步 🍅 欢迎点赞 👍 收藏 ⭐留言 📝
110 0
JavaScript入门保姆级教程 ——— 重难点详细解析(万字长文,建议收藏)
|
存储 编解码 Java
Java Web之Servlet中过滤器与监听器的解析及使用(附源码)
Java Web之Servlet中过滤器与监听器的解析及使用(附源码)
130 0
|
Java Spring
【杭州研发中心-后端二团队】springboot源码解析-监听器详情
简介:Spring的事件驱动模型由三部分组成: 事件: ApplicationEvent ,继承自JDK的 EventObject ,所有事件都要继承它,也就是被观察者 事件发布者: ApplicationEventPublisher 及 ApplicationEventMulticaster 接口,使用这个接口,就可以发布事件了 事件监听者: ApplicationListener ,继承JDK的 EventListener ,所有监听者都继承它,也就是我们所说的观察者,当然我们也可以使用注解 @EventListener ,效果是一样的
|
算法 调度 计算机视觉
YOLOX | MMDetection 复现保姆级解析
最近 YOLOX 火爆全网,速度和精度相比 YOLOv3、v4 都有了大幅提升,并且提出了很多通用性的 trick,同时提供了部署相关脚本,实用性极强。
796 0
YOLOX | MMDetection 复现保姆级解析
|
Java
JavaWeb-Servlet技术的监听器-解析与实例-网站在线用户信息与网页点击量(2)
JavaWeb-Servlet技术的监听器-解析与实例-网站在线用户信息与网页点击量
127 0
JavaWeb-Servlet技术的监听器-解析与实例-网站在线用户信息与网页点击量(2)
|
Java 应用服务中间件
JavaWeb-Servlet技术的监听器-解析与实例-网站在线用户信息与网页点击量(1)
JavaWeb-Servlet技术的监听器-解析与实例-网站在线用户信息与网页点击量
172 0

推荐镜像

更多