JavaWeb--Listener与Filter


JavaWeb--Listener与Filter

Listener

基本概念

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

Listener是JavaEE的规范,就是接口

监听器的作用是,监听某种变化(一般就是对象创建/销毁,属性变化),触发对应方法完成相应的任务

JavaWeb中的监听器(共八个),目前最常用的是ServletContextListener

监听接口

ServletContextListener

  • 作用:监听ServletContext创建或销毁(当我们Web应用启动时,就会创建ServletContext),即生命周期监听,应用场景(1)加载初始化的配置文件;比如spring的配置文件(2)任务调度(配合定时器Timer/TimerTask)

  • 方法:

    • voidcontextInitialized(ServletContextEventsce)创建Servletcontext时触发
    • voidcontextDestroyed(ServletContextEventsce)销毁Servletcontext时触发
  • 应用:

    /**
     * 1. 当一个类实现了 ServletContextListener
     * 2. 该类就是一个监听器
     * 3. 该类可以监听的事件 由该类实现的监听接口决定 ,比如 实现ServletContextListener
     *    , 则该类就可以监听 ServletContext对象的创建和销毁, 以此类推
     * 4. HspServletContextListener 就是一个监听者
     * 5. 当web应用启动时,就会产生 ServletContextEvent 事件, 会调用监听器的对应事件处理方法
     *    contextInitialized, 同时会传递 事件对象
     * 6. 程序员可以通过 ServletContextEvent 事件对象,来获取需要的信息, 然后再进行业务处理
     * 7. tomcat怎么知道这个监听器存在 ? 因为我们需要在web.xml中配置
     */
    public class HspServletContextListener implements ServletContextListener {
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
            ServletContext servletContext = servletContextEvent.getServletContext();
            System.out.println("HspServletContextListener 监听到 " +
                    servletContext + " 被创建..");
            //如果我们获取到ServletContext 对象..进行业务处理
    
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent servletContextEvent) {
            ServletContext servletContext = servletContextEvent.getServletContext();
            System.out.println("HspServletContextListener 监听到 " +
                    servletContext + " 被销毁..");
    
            //比如可以对servletContext 数据进行处理, 或者日志的管理...
            System.out.println("进行处理工作.....");
        }
    }
    
    ----
    web.xml
    
        
        
            com.hspedu.listener.HspServletContextListener
        
    

ServletContextAttributeListener

  • 作用:监听ServletContext属性变化

  • 方法:

    • void attributeAdded(ServletContextAttributeEvent event)添加属性时调用
    • void attributeReplaced(ServletContextAttributeEvent event)替换属性时调用
    • void attributeRemoved(ServletContextAttributeEvent event)移除属性时调用
  • 应用:

    public class HspServletContextAttributeListener implements ServletContextAttributeListener {
        @Override
        public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
    
            System.out.println("HspServletContextAttributeListener 监听到添加属性.."
                    + servletContextAttributeEvent.getName() + "=" + servletContextAttributeEvent.getValue() );
        }
    
        @Override
        public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
    
            System.out.println("HspServletContextAttributeListener 监听到删除属性.."
                    + servletContextAttributeEvent.getName() + "=" + servletContextAttributeEvent.getValue() );
        }
    
        @Override
        public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
    
            System.out.println("HspServletContextAttributeListener 监听到修改属性.."
                    + servletContextAttributeEvent.getName() + "=" + servletContextAttributeEvent.getValue() );
    
        }
    }
    
    -----
    web.xml
        
            com.hspedu.listener.HspServletContextAttributeListener
        
    

HttpSessionListener

  • 作用:监听Session创建或销毁,即生命周期监听

  • 方法:

    • void sessionCreated(HttpSessionEvent se)创 建session时 调 用
    • void sessionDestroyed(HttpSessionEvent se)销毁session时调用
  • 应用:

    public class HspHttpSessionListener implements HttpSessionListener {
        @Override
        public void sessionCreated(HttpSessionEvent httpSessionEvent) {
            HttpSession session = httpSessionEvent.getSession();
            //当session创建时,我们给它设置一个生命周期 30s
            session.setMaxInactiveInterval(30);
            System.out.println("HspHttpSessionListener 监听到 session创建= " +
                    session.getId());
            System.out.println("用户id=" + session.getId() + " 上线");
        }
    
        @Override
        public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
            HttpSession session = httpSessionEvent.getSession();
            System.out.println("HspHttpSessionListener 监听到 session销毁= " +
                    session.getId());
            System.out.println("用户id=" + session.getId() + " 离线");
        }
    }
    -------
        web.xml
        
            com.hspedu.listener.HspHttpSessionListener
        
    

HttpSessionAttributeListener

  • 作用:监听Session属性的变化
  • 方法:
    • void attributeAdded(ServletRequestAttributeEvent srae)添加属性时
    • void attributeReplaced(ServletRequestAttributeEvent srae)替换属性时
    • void attributeRemoved(ServletRequestAttributeEvent srae)移除属性时
  • 应用:同上

ServletRequestListener

  • 作用:监听Request创建或销毁,即Request生命周期监听
  • 方法:
    • void requestInitialized(ServletRequestEvent sre)创建request时
    • void requestDestroyed(ServletRequestEvent sre)销毁request时
  • 应用:同上

ServletRequestAttributeListener

  • 作用:监听Request属性变化
  • 方法:
    • void attributeAdded(ServletRequestAttributeEvent srae)添加属性时
    • void attributeReplaced(ServletRequestAttributeEvent srae)替换属性时
    • void attributeRemoved(ServletRequestAttributeEvent srae)移除属性时
  • 应用:同上

HttpSessionBindingListener感知监听器

HttpSessionActivationListener感知监听器

Filter

意义

概念

Filter 过滤器它是 JavaWeb 的三大组件之一(Servlet 程序、Listener 监听器、Filter 过滤器)

Filter 过滤器是 JavaEE 的规范,是接口

Filter 过滤器它的作用是:拦截请求,过滤响应

应用场景:1权限检查 2.日记操作 3.事务管理

原理

应用

/**
 * 1. filter在web项目启动时, 由tomcat 来创建filter实例, 只会创建一个
 * 2. 会调用filter默认的无参构造器, 同时会调用 init方法, 只会调用一次
 * 3. 在创建filter实例时,同时会创建一个FilterConfig对象,并通过init方法传入
 * 4. 通过FilterConfig对象,程序员可以获取该filter的相关配置信息
 * 5. 当一个http请求和该filter的的url-patter匹配时,就会调用doFilter方法
 * 6. 在调用doFilter方法时,tomcat会同时创建ServletRequest 和 ServletResponse 和 FilterChain对象, 并通过doFilter传入.
 * 7. 如果后面的请求目标资源(jsp,servlet..) 会使用到request,和 response,那么会继续传递
 * 8. 的提醒:到javaweb - ssm - springboot , 有 浏览器和 web服务器(tomcat)参与, 而这两个部分不是我们程序员自己写,所以理解起来比 java se要困难!!!
 */
public class ManageFilter implements Filter {

    private int count = 0;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        //当Tomcat 创建 Filter创建,就会调用该方法,进行初始化
        //提醒:回忆我们自己实现tomcat底层机制+servlet程序, 就会了然
        //
        System.out.println("ManageFilter init被调用...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws IOException, ServletException {

        System.out.println("ManageFilter doFilter() 被调用=" + (++count));

        //到每次调用该filter时,doFilter就会被调用

        //如果这里,没有调用继续请求的方法,则就停止
        //如果继续访问目标资源-> 等价于放行

        //老师说明:在调用过滤器前,servletRequest对象=request已经被创建并封装
        //所以:我们这里就可以通过servletRequest获取很多信息, 比如访问url , session
        //比如访问的参数 ... 就可以做事务管理,数据获取,日志管理等
        //获取到session
        //可以继续使用 httpServletRequest 方法.
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        System.out.println("输入密码=" + httpServletRequest.getParameter("password"));
        HttpSession session = httpServletRequest.getSession();
        //获取username session对象, 还可以继续使用
        Object username = session.getAttribute("username");
        if (username != null) {
            //解读filterChain.doFilter(servletRequest, servletResponse)
            //1. 继续访问目标资源url
            //2. servletRequest 和 servletResponse 对象会传递给目标资源/文件
            //3. 一定要理解filter传递的两个对象,再后面的servlet/jsp 是同一个对象(指的是在一次http请求)
            System.out.println("servletRequest=" + servletRequest);
            System.out.println("日志信息==");
            System.out.println("访问的用户名=" + username.toString());
            System.out.println("访问的url=" + httpServletRequest.getRequestURL());
            System.out.println("访问的IP=" + httpServletRequest.getRemoteAddr());
            filterChain.doFilter(servletRequest, servletResponse);
        } else {//说明没有登录过..回到登录页面
            servletRequest.getRequestDispatcher("/login.jsp").
                    forward(servletRequest, servletResponse);
        }
    }

    @Override
    public void destroy() {
        //当filter被销毁时,会调用该方法
        System.out.println("ManageFilter destroy()被调用..");
    }
}
------------------
    web.xml
	
    
        ManageFilter
        com.hspedu.filter.ManageFilter
    
    
        ManageFilter
        /manage/*
    

url-pattern

  1. url-pattern : Filter 的拦截路径, 即浏览器在请求什么位置的资源时,过滤器会进行拦截过滤

  2. 精确匹配 /a.jsp 对应的 请求地址 http://ip[域名]:port/工程路径/a.jsp 会拦截

  3. 目录匹配 /manage/*对应的 请求地址 http://ip[域名]:port/工程路径/manage/xx , 即 web 工程 manage 目录下所有资源 会拦截

  4. 后缀名匹配 *.jsp 后缀名可变,比如 *.action *.do 等等对应的请求地址 http://ip[域名]:port/工程路径/xx.jsp , 后缀名为 .jsp 请求会拦截

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

生命周期

FilterConfig

  1. FilterConfig 是 Filter 过滤器的配置类
  2. Tomcat 每次创建 Filter 的时候,也会创建一个 FilterConfig 对象,这里包含了 Filter 配置文件的配置信息。
  3. FilterConfig 对象作用是获取 filter 过滤器的配置内容
应用
public class HspFilterConfig implements Filter {

    private String ip; //从配置获取的ip

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("HspFilterConfig init() 被调用..");
        //通过filterConfig 获取相关的参数
        String filterName = filterConfig.getFilterName();
        ip = filterConfig.getInitParameter("ip");
        ServletContext servletContext = filterConfig.getServletContext();
        //可以获取到该filter所有的配置参数名
        Enumeration initParameterNames =
                filterConfig.getInitParameterNames();

        //遍历枚举
        while (initParameterNames.hasMoreElements()) {
            System.out.println("名字=" + initParameterNames.nextElement());
        }

        System.out.println("filterName= " + filterName);
        System.out.println("ip= " + ip);
        System.out.println("servletContext= " + servletContext);


    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        //通过forbidden ip 来进行控制
        //先获取到访问ip
        String remoteAddr = servletRequest.getRemoteAddr();
        if(remoteAddr.contains(ip)) {
            System.out.println("封杀该网段..");
            servletRequest.getRequestDispatcher("/login.jsp").
                    forward(servletRequest,servletResponse);
            return; //直接返回
        }

        //继续访问目标资源
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {

    }
}


--------------------
    web.xml
    
        HspFilterConfig
        com.hspedu.filter.HspFilterConfig
        
        
            ip
            127.0
        
        
            port
            8888
        
        
            email
            hsp@sohu.com
        
    
    
        HspFilterConfig
        /abc/*
    

FilterChain过滤器链

一句话: FilterChain:在处理某些复杂业务时,一个过滤器不够,可以设计多个过滤器共同完成过滤任务,形成过滤器链

原理

应用

public class AFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("AFilter---> 线程id=" +
                Thread.currentThread().getId());

        System.out.println("AFilter doFilter 的前置代码...");
        System.out.println("执行 AFilter doFilter()");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("AFilter doFilter 的后置代码...");
    }

    @Override
    public void destroy() {

    }
}

public class BFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("BFilter---> 线程id=" +
                Thread.currentThread().getId());

        System.out.println("BFilter doFilter 的前置代码...");

        System.out.println("执行 BFilter doFilter()");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("BFilter doFilter 的后置代码...");
    }

    @Override
    public void destroy() {

    }
}

------------------------
    web.xml
    
        AFilter
        com.hspedu.filter.AFilter
    
    
        AFilter
        /admin/*
    

    
        BFilter
        com.hspedu.filter.BFilter
    
    
        BFilter
        /admin/*
    

注意事项

  1. 多个 filter 和目标资源在一次 http 请求,在同一个线程中
  2. 当一个请求 url 和 filter 的 url-pattern 匹配时, 才会被执行, 如果有多个匹配上,就会顺序执行,形成一个 filter 调用链(底层可以使用一个数据结构搞定)
  3. 多个 filter 共同执行时,因为是一次 http 请求, 使用同一个 request 对象
  4. 多个 filter 执行顺序,和 web.xml 配置顺序保持一致.
  5. chain.doFilter(req, resp)方法 将执行下一个过滤器的 doFilter 方法, 如果后面没有过滤器,则执行目标资源。
  6. 小结:注意执行过滤器链时, 顺序是(用前面的案例分析) Http请求 -> A 过滤器 dofilter() -> A 过滤器前置代码 -> A 过滤器 chain.doFilter() -> B 过滤器 dofilter() -> B 过滤器前置代码 -> B过滤器 chain.doFilter() -> 目标文件 -> B过滤器后置代码 -> A过滤器后置代码 ->返回给浏览器页面/数据