今日内容

  • Filter 过滤器
  • Listener 监听器

1 Filter 过滤器

1.1 概念

  • Filter是我们web开发中的过滤器,可以根据“拦截规则”,拦截请求。然后,根据自己的需要,决定是否放行。
  • 过滤器的作用:

    • 一般用于完成通用的操作。如:登录验证、统一编码处理、敏感字符过滤

1.2 使用步骤

  1. 定义一个类,实现Filter接口
  2. 重写所有抽象方法
  3. 在doFilter方法中,自定义规则决定是否放行
  4. 配置过滤器的过滤规则

1.3 快速入门

@WebFilter("/*")    //过滤规则:拦截所有请求
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        /*
            if(如果符合我们自己定义的条件){
                //放行
                filterChain.doFilter(servletRequest,servletResponse);
            }
         */
        filterChain.doFilter(servletRequest,servletResponse);
    }
    @Override
    public void destroy() {}
}

1.4 过滤器执行流程

  1. 执行过滤器
  2. 执行放行后的资源
  3. 回来执行过滤器放行代码下边的代码

1.5 过滤器生命周期方法

  1. init:在服务器启动后,会创建Filter对象,然后调用init方法。只执行一次。用于加载资源
  2. doFilter:每一次请求被拦截资源时,会执行。执行多次
  3. destroy:在服务器关闭后,Filter对象被销毁。如果服务器是正常关闭,则会执行destroy方法。只执行一次。用于释放资源

1.6 过滤器配置

  • 拦截路径配置

    • 具体资源路径: /index.jsp 只有访问index.jsp资源时,过滤器才会被执行
    • 拦截目录: /user/* 访问/user下的所有资源时,过滤器都会被执行
    • 后缀名拦截: *.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行
    • 拦截所有资源:/* 访问所有资源时,过滤器都会被执行
  • 拦截方式配置:资源被访问的方式

    • 注解配置:

      • 设置dispatcherTypes属性

        • REQUEST:默认值。浏览器直接请求资源
        • FORWARD:转发访问资源
        • INCLUDE:包含访问资源
        • ERROR:错误跳转资源
        • ASYNC:异步访问资源
      • web.xml配置
      <filter>
          <filter-name>aaa</filter-name>
          <filter-class>com.itheima.web.filter.MyFilter</filter-class>
      </filter>
      <filter-mapping>
          <filter-name>aaa</filter-name>
          <url-pattern>拦截路径</url-pattern>
          <dispatcher>拦截方式</dispatcher>
      </filter-mapping>
      //注意:dispatcher可以省略,默认是REQUEST,当然,dispatcher也可以写多个。

1.7 过滤器链(配置多个过滤器)

  • 执行顺序:如果有多个过滤器 过滤器1和过滤器2
1. 过滤器1
2. 过滤器2
3. 资源执行
4. 过滤器2
5. 过滤器1 
  • 过滤器先后顺序问题:

    • 注解配置:按照类名的字符串比较规则比较,值小的先执行

      • 如: AFilter 和 BFilter,AFilter就先执行了。
    • web.xml配置: <filter-mapping>谁定义在上边,谁先执行

1.8 案例

  • 登录验证
  • 需求:

    • 访问案例的资源。验证其是否登录
    • 如果登录了,则直接放行。
    • 如果没有登录,则跳转到登录页面,提示"您尚未登录,请先登录"。
  • 代码实现:
package me.ffis.web.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

// 验证是否登录Fliter
@WebFilter("/*") // 过滤所有请求资源
public class LoginFilter implements Filter {
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        // 1.先进行强制转换
        HttpServletRequest request = (HttpServletRequest) req;
        // 2.获取请求路径uri
        String uri = request.getRequestURI();
        // 3.判断路径是否包含登录相关页面
        if (uri.contains("login.jsp") || uri.contains("AdminLoginServlet") || uri.contains("/css/") || uri.contains("/fonts/") || uri.contains("/js/") || uri.contains("GetCheckCode")) {
            // 如果包含则放行
            chain.doFilter(req, resp);
        } else {
            // 如果不包含,判断是否登录
            Object admin = request.getSession().getAttribute("admin");
            if (admin != null) {
                // 如果已经登录则放行
                chain.doFilter(req, resp);
            } else {
                // 否则跳转回登录页面
                request.setAttribute("loginError", "您尚未登录!请先登录!");
                request.getRequestDispatcher("/login.jsp").forward(request, resp);
            }
        }
    }
    
    public void init(FilterConfig config) throws ServletException {
    }
    
}

1.9 增强类的方法

  • 继承+重写:

    动态代理: Object obj = Proxy.newProxyInstance(类的加载器, 接口数据, new InvocationHandler() {});

1.10 动态代理

  • 语法:Object obj = Proxy.newProxyInstance(参数1, 参数2, 参数3);

    • //参数1: ClassLoader loader, 类的加载器: 一般固定为: 被代理的对象.getClass().getClassLoader()
    • //参数2: Class<?>[] interfaces,接口数据: 一般固定为: 被代理的对象.getClass().getInterfaces();
    • //参数3: InvocationHandler h 处理器: 真实的代理动作()
  • 注意:

    • 参数3,在创建InvocationHandler的子类对象时,需要重写一个方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //真实的代理动作
        return null;
    }
    // proxy:    代理对象
    // method:    代理对象正在执行的方法
    // args:     代理对象所调用的方法的参数

2 Listener 监听器

2.1 概念:

  • web的三大组件之一。
  • 事件监听机制

    • 事件 :一件事情
    • 事件源 :事件发生的地方
    • 监听器 :一个对象
    • 注册监听:将事件、事件源、监听器绑定在一起。 当事件源上发生某个事件后,执行监听器代码

2.2 步骤

  1. 创建一个类,实现ServletContextListener
  2. 重写方法
  3. 注册监听(@WebListener)

2.3 代码

@WebListener
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //当服务器启动的时候执行该段代码
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        //当服务器正常关闭的时候执行该段代码
    }
}

2.4 配置

  • 注解配置:

    • @WebListener
  • XML配置
<listener>
    <listener-class>me.ffis.web.listener.ContextLoaderListener</listener-class>
</listener>