Filter与Listener入门

Filter:过滤器

Filter概述

  • JavaWeb三大组件之一,与Servlet很相似,Servlet处理请求,Filter拦截请求的。当客户端发送请求的时候,会经过过滤器,然后才能到servlet,当我们的servlet处理完请求之后,我们的response还是先经过过滤器才能到达客户端

  • Filter也称之为过滤器,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

  • Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。

alt

Filter使用步骤

  1. 定义一个类实现javax.servlet下的Filter接口,并重写接口中的所有抽象方法
    • 自定义filter模板:settings->Editor->File and Code Templates->Other选项卡->Web->Filter Annotated Class.java
    package cn.simplek9.web.filter;
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import java.io.IOException;
    
    public class filter2 implements Filter {//注意是javax.servlet下的Filter
        public void destroy() {
        }
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
            //去的时候对request对象请求消息进行操作
            System.out.println("filter2被执行了......");
            //放行,没有该语句无法访问被拦截的资源
            chain.doFilter(req, resp);
            //回来的时候对response对象响应消息进行操作
            System.out.println("filter2回来了......");
        }
        public void init(FilterConfig config) throws ServletException {
        }
    }
    
  2. 配置Filter
  • 拦截路径配置:
    1. 具体资源路径: /index.jsp 只有访问index.jsp资源时,过滤器才会被执行
    2. 拦截目录: /user/* 访问/user下的所有资源时,过滤器都会被执行
    3. 后缀名拦截: *.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行
    4. 拦截所有资源:/* 访问所有资源时,过滤器都会被执行
  • 拦截方式配置:资源被访问的方式 注解配置设置dispatcherTypes属性,web.xml配置设置的子标签
    1. REQUEST:默认值。浏览器直接请求资源
    2. FORWARD:转发访问资源
    3. INCLUDE:包含访问资源
    4. ERROR:错误跳转资源
    5. ASYNC:异步访问资源
  • 配置拦截路径(必选),配置拦截方式:资源被访问的方式(可选)
  • 方法一:在项目的web/WEB-INF/web.xml的根标签中配置
    <filter>
        <filter-name>demo1</filter-name><!--给该filter所起的名字-->
        <filter-class>cn.simplek9.web.filter.filter</filter-class><!--src下的全类名-->
    </filter>
    <filter-mapping>
        <filter-name>demo1</filter-name>
        <url-pattern>/*</url-pattern><!--该filter的拦截路径-->
        <dispatcher>REQUEST</dispatcher><!--配置拦截方式-->
    </filter-mapping>
    
  • 方法二:类前使用@WebFilter("拦截路径")注解配置
    //仅配置拦截路径
    //@WebFilter("/hello.jsp")//拦截index.jsp资源
    //@WebFilter("/user/*")//拦截/user下的所有资源
    //@WebFilter("*.jsp")//拦截所有jsp资源
    @WebFilter("/*")//拦截所有资源
    
    //配置拦截路径与拦截方式
    //浏览器直接请求index.jsp资源时,该过滤器会被执行
    //@WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.REQUEST)
    //只有转发访问index.jsp时,该过滤器才会被执行
    //@WebFilter(value="/index.jsp",dispatcherTypes = DispatcherType.FORWARD)
    //浏览器直接请求index.jsp或者转发访问index.jsp时,该过滤器才会被执行
    @WebFilter(value="/index.jsp",dispatcherTypes ={DispatcherType.FORWARD,DispatcherType.REQUEST})
    

Filter生命周期方法

  1. init:在服务器启动后,会创建Filter对象,然后调用init方法。(一次)
    • 可以读取web.xml文件中Servlet过滤器的初始化参数。
  2. doFilter:每一次请求被拦截资源时执行。(多次)
    • 调用chain.doFilter(request,response)方法完成对下一个过滤器的调用,如果没有下一个过滤器,则把客户请求传给相应的web组件(放行)。
  3. destroy:在销毁过滤器实例之前调用该方法,以释放占用的资源(一次)

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

  • 执行顺序:如果有两个过滤器:过滤器1和过滤器2 过滤器1 ->过滤器2->资源执行->过滤器2->过滤器1
  • 过滤器先后顺序问题:
    1. 注解配置:按照类名的字符串比较字典序,字典序小的先执行
      • 如: AFilter 和 BFilter,AFilter就先执行了。
    2. web.xml配置: 谁定义在上边,谁先执行

Filter登录验证

登录成功时,会在 session 中存入 login_msg 值,所以可以以此判读用户是否登录

package cn.itcast.web.filter;

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

/**
 * 登录验证的过滤器
 */
@WebFilter("/*")
public class LoginFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //0.强制转换,HttpServletRequest是ServletRequest的子类,因为要用到Http相应请求和响应方法,所以要强转
        HttpServletRequest request = (HttpServletRequest) req;
        //1.获取资源请求路径
        String uri = request.getRequestURI();
        //2.判断是否包含登录相关资源路径,要注意排除掉 css/js/图片/验证码等资源
        if(uri.contains("/login.jsp") || uri.contains("/loginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/fonts/") || uri.contains("/checkCodeServlet")  ){
            //包含,用户就是想登录。放行
            chain.doFilter(req, resp);
        }else{
            //不包含,需要验证用户是否登录
            //3.从获取session中获取user
            Object user = request.getSession().getAttribute("user");
            if(user != null){
                //登录了。放行
                chain.doFilter(req, resp);
            }else{
                //没有登录。跳转登录页面
                request.setAttribute("login_msg","您尚未登录,请登录");
                request.getRequestDispatcher("/login.jsp").forward(request,resp);
            }
        }
        // chain.doFilter(req, resp);
    }
    public void init(FilterConfig config) throws ServletException {
    }
    public void destroy() {
    }
}

Listener:监听器

Listener概述

  • JavaWeb三大组件之一,监听器就是监听某个域对象的的状态变化的组件
  • 监听器的相关概念:
    • 事件源:被监听的对象(三个域对象 request、session、servletContext)
    • 监听器:监听事件源对象事件源对象的状态的变化都会触发监听器(6+2)
    • 注册监听器:将监听器与事件源进行绑定
    • 响应行为:监听器监听到事件源的状态变化时所涉及的功能代码(程序员编写代码)

alt

Listener使用步骤

  1. 定义一个类实现ServletContextListener接口,并重写接口中的所有抽象方法
    package cn.simplek9.web.listener;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import javax.servlet.annotation.WebListener;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    @WebListener//不需要路径
    public class listener implements ServletContextListener {
        /*
        * 监听ServletContext对象创建的。会在服务器启动后自动调用
        * */
        @Override
        public void contextInitialized(ServletContextEvent sce) {//ServletContext对象创建后会调用该方法
            //加载资源文件
            //1.获取ServletContext对象
            ServletContext servletContext = sce.getServletContext();
            //2.加载资源文件
            String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
            //3.获取真实路径
            String realPath = servletContext.getRealPath(contextConfigLocation);
            //4.加载进内存
            try {
                FileInputStream fis = new FileInputStream(realPath);
                System.out.println(fis);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            System.out.println("ServletContext对象被创建了");
        }
        /*
        * 在服务器关闭后,ServletContext对象被销毁。会在服务器正常关闭后自动调用
        * */
        @Override
        public void contextDestroyed(ServletContextEvent sce) {//ServletContext对象被销毁之前会调用该方法
            System.out.println("ServletContext对象被销毁了");
        }
    }
    
  2. 配置Listener
    • 方法一:在项目的web/WEB-INF/web.xml的根标签中配置
      <listener>
          <listener-class>cn.simplek9.web.listener.listener</listener-class>
      </listener>
      
      <!--指定初始化参数-->
      <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/classes/config.xml</param-value>
      </context-param>
      
    • 方法二:类前使用@WebListener注解配置
      @WebListener