一、过滤器(Filter)

1.1 过滤器基本特点

  • 它会在一组资源(jsp、servlet、css、html等等)的前面执行
  • 它可以让请求得到目标资源,也可以不让请求得到
    • 过滤器有拦截请求的能力

1.2 如何编写一个过滤器

  1. 写一个类实现 Filter 接口
  2. 在 web.xml 中进行配置

1.2.1 Filter 生命周期的三大方法

过滤器的三个生命周期,以下为三个方法

  1. import javax.servlet.*;
  2. import java.io.IOException;
  3. public class AFilter implements Filter {
  4. /**
  5. * 创建之后马上执行,用来初始化!
  6. * Filter 会在服务器启动时创建
  7. * */
  8. @Override
  9. public void init(FilterConfig filterConfig) throws ServletException {
  10. }
  11. /**
  12. * 每次过滤都会执行
  13. * */
  14. @Override
  15. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  16. }
  17. /**
  18. * 销毁之前执行,用来做对非内存资源进行释放
  19. * */
  20. @Override
  21. public void destroy() {
  22. }
  23. }

Filter 是单例的

1.2.2 web.xml 配置过滤器

这里我们拦截 AServlet 的访问

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. <filter>
  7. <filter-name>AFilter</filter-name>
  8. <filter-class>cn.study.web.filter.AFilter</filter-class>
  9. </filter>
  10. <filter-mapping>
  11. <filter-name>AFilter</filter-name>
  12. <!-- /* 访问任何资源都会经过过滤器
  13. /web/*
  14. /AServlet 拦截指定的内容
  15. -->
  16. <url-pattern>/AServlet</url-pattern>
  17. </filter-mapping>
  18. </web-app>

1.2.3 run

image.png
成功被拦截

image.png

1.3 Filter 使用场景

借助 Filter 的特性,在 Tomcat 服务器运行时,就初始化的特性,我们可以设置全局的编码格式

  1. 在 web.xml 中配置过滤器以及参数 ```xml <?xml version=”1.0” encoding=”UTF-8”?>

CharacterEncodingFilter cn.gorit.filter.CharacterEncodingFilter encoding utf-8 CharacterEncodingFilter /*


2. 编写 CharacterEncodingFilter
```java
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// 初始化编码
@WebFilter(filterName = "CharacterEncodingServlet")
public class CharacterEncodingFilter implements Filter {
    private String encoding;

    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)resp;
        request.setCharacterEncoding(encoding);
        response.setCharacterEncoding(encoding);
        response.setContentType("text/html;charset="+encoding);
        chain.doFilter(req, resp);
    }

    public void init(FilterConfig config) throws ServletException {
        encoding = config.getInitParameter("encoding"); // 获取 xml 中的参数的值
    }
}

二、理解 Filter 参数

2.1 FilterConfig

  • 获取初始化参数:getInitParameter()
  • 获取过滤器名称:getFilterName()
  • 获取 application:getServletContext()

2.2 FilterChain

  • doFilter(ServletRequest, ServletResponse) 放行,相当于执行了目标的 servlet 的 service 方法!
  1. 修改 doFilter 的内容

     @Override
     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
         System.out.println("拦截你");
         filterChain.doFilter(servletRequest, servletResponse); // 放行
         System.out.println("可以访问啦");
     }
    
  2. 访问 AServlet

image.png

  1. 然后可以看到 AServlet 可以执行了

image.png

三、多过滤器拦截

FilterChain # doFilter 方法: 执行目标资源,或者执行下一个过滤器! 如果没有下一个过滤器,那么执行的是目标资源,如果有,那么执行下一个过滤器

3.1 过滤器的四种拦截方式

  1. 请求 DISPATCHER
  2. 转发 FORWARD
  3. 包含 INCLUDE
  4. 错误 ERROR

在 filter-mapping 中进行配置

   <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>cn.study.web.filter.AFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AFilter</filter-name>
        <!-- /* 访问任何资源都会经过过滤器
            /web/*
            /AServlet 拦截指定的内容
        -->
        <url-pattern>/AServlet</url-pattern>
        <dispatcher>REQUEST</dispatcher> <!-- 默认的! -->
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

3.2 多个过滤器的执行顺序

在 filter-mapping 中的配置顺序,决定了执行顺序

   <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>cn.study.web.filter.AFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AFilter</filter-name>
        <url-pattern>/AServlet</url-pattern>
    </filter-mapping>

   <filter>
        <filter-name>BFilter</filter-name>
        <filter-class>cn.study.web.filter.BFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>BFilter</filter-name>
        <url-pattern>/BServlet</url-pattern>
    </filter-mapping>

四、filter 小结

4.1 过滤器的应用场景

  • 执行目标资源之前做预处理工作,例如设置编码,这种试通常都会放行,只是在目标资源执行之前做一些准备工作;。(几乎所有的 Servlet 都要设置 request.setCharcterEncoding() 可以把它放到一个 Filter中)
  • 通过条件判断是否放行,例如校验当前用户是否已经登录,或者用户IP是否已经被禁用; +
  • 在目标资源执行后,做一些后续的特殊处理工作,例如把目标资源输出的数据进行处理;。(回程拦截)

4.2 filter 三个方法

  • void init(FilterConfig filterConfig) Tomcat 启动时被调用
  • void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 每次请求时都调用该方法
  • void destroy() Tomcat 关闭时调用

4.3 四种拦截方式

  • 拦截请求 DISPATCHER
  • 拦截转发 FORWARD
  • 拦截包含 INCLUDE
  • 拦截错误 ERROR

4.4 两个 filter 参数

  • filterConfig(对比 ServletConfig)
  • filterChain (放行)

五、Filter 案例

5.1 分 ip 统计网站访问次数

ip count
192.168.1.112 5
192.168.1.113 52

统计工作要在所有资源之前执行,那么就可以放到 Filter 中。
这个过滤器不打算做拦截操作,因为作为统计使用
用什么东西统计数据:Map
整个网站只需要一个 Map 即可!
Map 什么时候创建(使用 ServletContextListener,在服务器启动时完成创建,并只在到 ServletContext中), Map 保存到哪里(Map 保存到 ServletContext)

  • Map 需要在 Filter 中保存数据
  • Map 需要在页面中使用,打印 Map中的数据

六、监听器(Listener)

监听器

  1. 它是一个接口,内容由我们来实现
  2. 他需要注册
  3. 监听器中的方法,会在特殊事件发生时调用

观察者

  • 事件源(小偷)
  • 事件(偷东西)
  • 监听器(警察,监听器的方法,抓捕)

6.1 监听器概述

6.1.1 事件源:三大域

  1. ServletContext

    1. 生命周期监听:ServletContextListener,它有两个方法,
      1. 一个在出生时调用:void contextInitialized(ServletContextEvent sce) 创建 ServletContext 时
      2. 一个在死亡时调用:void contextDestroyed(ServletContextEvent sce) 销毁 ServletContext 时
    2. 属性监听:ServletContextAttribute:它有三个方法:
      1. 一个在添加属性时调用: void attributeAdded(SenvletContextAttributeEvent event): 添加属性时;
      2. 一个在替换属性时调用: void attributeReplaced(SevletContextAttributeEvent event): 替换属性时;
      3. 最后一个在移除属性时调用: void atributeRemoved(ServletContexttributeEvent event): 移除属性时;
  2. HTTPSession

    1. 生命周期监听:HTTPSessiontListener,它有两个方法,
      1. 一个在出生时调用:void sessionCreated(HttpSessionEvent se) 创建 session 时
      2. 一个在死亡时调用:void sessionDestroyed(HttpSessionEvent se) 销毁 session 时
    2. 属性监听:HTTPSessiontAttribute:它有三个方法:
      1. 一个在添加属性时调用: void attributeAdded(HttpSessionBindingEvent event): 添加属性时;
      2. 一个在替换属性时调用: void atributeReplaced(HittpSessionBindingEvent event):替换属性时
      3. 最后一个在移除属性时调用: void attributeRemoved(HttpSessionBindingEvent event):移除属性时
  3. ServletRequest

    1. 生命周期监听:ServletRequestListener,它有两个方法,
      1. 一个在出生时调用:void requestInitialized(ServletRequestEvent sre)
      2. 一个在死亡时调用:void requestDestroyed(ServletRequestEvent sre)
    2. 属性监听:ServletRequestAttribute:它有三个方法:
      1. 一个在添加属性时调用: vold attributeAdded(ServletRequestAttributeEvent sree):添加属性时
      2. 一个在替换属性时调用: void attributeReplaced(ServletRequestAttributeEvent srae):替换属性时
      3. 最后一个在移除属性时调用: void attributeRemoved(ServletRequestAttributeEvent srae): 移除属性时

6.1.2 JavaWeb 中完成编写监听器:

  • 写一个监听器类,要求必须实现某个监听器的接口
  • 注册:是在 web.xml 中实现注册

实现 ServletContextListener 接口

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * ServletContext 生死监听
 可以在监听器中存放一些 Tomcat启动时要完成的代码
 * */
public class AListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("我是监听器 AListener"); // 出生了
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println(" 我要死亡了");
    }
}

注册

<?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>cn.study.web.listeber.AListener</listener-class>
    </listener>
</web-app>

监听器触发
image.png

6.1.3 事件对象

  • ServletContextEvent:
    • ServletContext getServletContext()
  • HttpSessionEvent::
    • HttpSession getSession()
  • ServletRequestEvent
    • ServletContext getServletContext()
    • ServletRequest getServletRequestEvent()
  • SenvletContextAttributeEvent
    • ServletContext getServletContext()
    • String getName() 获取属性名
    • Object getValue() 获取属性值
  • HttpSessionBindingEvent
  • ServletRequestAttributeEvent

6.1.4 感知监听 (与 HTTPSession 相关)

  • 它用来添加到 javaBean上,而不是添加到三大域上
  • 这两个监听器无需在 web.xml 中注册

HttpSessionBindingListener:添加到 JavaBean上,javaBean 就知道自己是否添加到 session 中了

  1. 编写 User 并实现 HttpSessionBindingListener 接口 ```java import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener;

public class User implements HttpSessionBindingListener { private String username; private String password;

public User() {
}

public User(String username, String password) {
    this.username = username;
    this.password = password;
}

// getter 和 setter 省略

@Override
public String toString() {
    return "User{" +
            "username='" + username + '\'' +
            ", password='" + password + '\'' +
            '}';
}

@Override
public void valueBound(HttpSessionBindingEvent event) {
    System.out.println("啊~, session 添加了我");
}

@Override
public void valueUnbound(HttpSessionBindingEvent event) {
    System.out.println("哇, session 抛弃了我");
}

}


2. 分别编写 add.jsp 和 remove.jsp

add.jsp
```jsx
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page  import="cn.study.web.listener.User" %>
<html>
<head>
    <title>添加 session</title>
</head>
<body>
    <%
        User user = new User("user","111");
        session.setAttribute("user",user);
    %>
</body>
</html>

remove.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>remove.jsp</title>
</head>
<body>
    <h1>删除 session</h1>
    <%
        session.removeAttribute("user");
    %>
</body>
</html>
  1. 然后运行 Tomcat 分别运行 add.jsp 和 remove.jsp

image.png