监听器/过滤器(Servlet Listener / Filte

作者: 奋斗的老王 | 来源:发表于2017-03-05 12:45 被阅读350次

    监听器(listener)

    • 监听器简介 :

      • 监听器就是一个实现特定接口的普通java程序,这个程序专门用于监听另一个java对象的方法调用或属性改变,当被监听对象发生上述事件后,监听器某个方法将立即被执行。
      • 主要是用来监听特定对象的创建或销毁、属性的变化的!是一个实现特定接口的普通java类!
      • 对象:自己创建自己用 (不用监听), 别人创建自己用 (需要监听)
    • 监听器的监听对象

      • 在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别为ServletContext, HttpSessionServletRequest 这三个域对象
      • Servlet规范针对这三个对象上的操作,又把这多种类型的监听器划分为三种类型
        1. 监听三个域对象创建和销毁的事件监听器
        2. 监听域对象中属性的增加和删除的事件监听器
        3. 监听绑定到 HttpSession 域中的某个对象的状态的事件监听器 (查看API文档)
    • 监听器接口
      一、监听对象创建/销毁的监听器接口

      1. 监听ServletContext域对象创建和销毁
      - ServletContextListener 接口用于监听 ServletContext 对象的创建和销毁事件。
      - 当 ServletContext 对象被创建时,激发contextInitialized (ServletContextEvent sce)方法
      - 当 ServletContext 对象被销毁时,激发contextDestroyed(ServletContextEvent sce)方法。
      - servletContext域对象何时创建和销毁:
        1. 创建:服务器启动针对每一个web应用创建servletcontext
        2. 销毁:服务器关闭前先关闭代表每一个web应用的servletContext
      
      1. 监听HttpSession域对象创建和销毁
      - HttpSessionListener接口用于监听HttpSession的创建和销毁
      - 创建一个Session时,sessionCreated(HttpSessionEvent se) 方法将会被调用
      - 销毁一个Session时,sessionDestroyed (HttpSessionEvent se) 方法将会被调用(此处复习session对象,写多个servlet都去getSession,看session的创建)
      - Session域对象创建和销毁的时机
        1. 创建:用户每一次访问时,服务器创建session
        2. 销毁:如果用户的session 30分钟没有使用,服务器就会销毁session,我们在web.xml里面也可以配置session失效时间
      
      1. 监听HttpRequest域对象创建和销毁
      - ServletRequestListener 接口用于监听ServletRequest 对象的创建和销毁
      - Request 对象被创建时,监听器的requestInitialized方法将会被调用
      - Request对象被销毁时,监听器的requestDestroyed方法将会被调用(此处复习request对象,在浏览器窗口中多次刷新访问servlet,看request对象的创建和销毁,并写一个servlet,然后用sendRedirect、forward方式跳转到其它servlet,查看request对象的创建和消耗)
      - servletRequest域对象创建和销毁的时机:
        1. 创建:用户每一次访问,都会创建一个reqeust
        2. 销毁:当前访问结束,request对象就会销毁
      
      1. 代码 :
      - ServletRequestListener(监听request对象的创建或销毁)
      
    package anderson.sh.life;
    import javax.servlet.ServletRequestEvent;
    import javax.servlet.ServletRequestListener;
    // 监听request对象的创建或销毁
    public class MyRequestListener implements ServletRequestListener{
       // 对象销毁
       @Override
       public void requestDestroyed(ServletRequestEvent sre) {
           // 获取request中存放的数据
           Object obj = sre.getServletRequest().getAttribute("cn");
           System.out.println(obj);
           System.out.println("MyRequestListener.requestDestroyed()");
       }
       // 对象创建
       @Override
       public void requestInitialized(ServletRequestEvent sre) {
           System.out.println("MyRequestListener.requestInitialized()");
       }
    }
    
      - HttpSessionListener(监听session对象的创建或销毁)
    
    package anderson.sh.life;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;
    /**
    监听Session对象创建、销毁
     */
    public class MySessionListener implements HttpSessionListener{
        // 创建
        @Override
        public void sessionCreated(HttpSessionEvent se) {
            System.out.println("MySessionListener.sessionCreated()");
        }
        // 销毁
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            System.out.println("MySessionListener.sessionDestroyed()");
        }
    }
    
      - ServletContextListener(监听servletContext对象的创建或销毁)
    
    package anderson.sh.life;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    /**
    监听ServletContext对象创建或销毁
     */
    public class MyServletContextListener implements ServletContextListener{
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
            System.out.println("MyServletContextListener.contextDestroyed()");
        }
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            System.out.println("1..........MyServletContextListener.contextInitialized()");
        }
    }
    

    二、监听对象属性的变化
    - Servlet规范定义了监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信息事件的监听器, 这三个监听器接口分别是ServletContextAttributeListener, HttpSessionAttributeListener ServletRequestAttributeListener, 这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同

    - attributeAdded 方法
      - 当向被监听器对象中增加一个属性时,web容器就调用事件监听器的 attributeAdded 方法进行相应,这个方法接受一个事件类型的参数,监听器可以通过这个参数来获得正在增加属性的域对象和被保存到域中的属性对象
      - 各个域属性监听器中的完整语法定义为:
        1. `` public void attributeAdded(ServletContextAttributeEvent scae) ``
        2. `` public void attributeReplaced(HttpSessionBindingEvent  hsbe) ``
        3. `` public void attributeRmoved(ServletRequestAttributeEvent srae) ``
    
    - attributeRemoved 方法
      - 当删除被监听对象中的一个属性时,web 容器调用事件监听器的这个方法进行相应
      - 各个域属性监听器中的完整语法定义为:
        1. `` public void attributeRemoved(ServletContextAttributeEvent scae) ``
        2. `` public void attributeRemoved (HttpSessionBindingEvent** ** hsbe) ``
        3. `` public void attributeRemoved (ServletRequestAttributeEvent srae) ``
    
    - attributeReplaced 方法
      - 当监听器的域对象中的某个属性被替换时,web容器调用事件监听器的这个方法进行相应各个域属性监听器中的完整语法定义为:
        1. `` public void attributeReplaced(ServletContextAttributeEvent scae) ``
        2. `` public void attributeReplaced (HttpSessionBindingEvent** ** hsbe) `` 
        3. `` public void attributeReplaced (ServletRequestAttributeEvent srae) ``
    
    - 代码: 
    
    package anderson.sh.attr;
    import javax.servlet.http.HttpSession;
    import javax.servlet.http.HttpSessionAttributeListener;
    import javax.servlet.http.HttpSessionBindingEvent;
    /** 
    监听session对象属性的变化
     */
    public class MySessionAttrListener implements HttpSessionAttributeListener {
        // 属性添加
        @Override
        public void attributeAdded(HttpSessionBindingEvent se) {
            // 先获取session对象
            HttpSession session = se.getSession();
            // 获取添加的属性
            Object obj = session.getAttribute("userName");
            // 测试
            System.out.println("添加的属性:" + obj);
        }
        // 属性移除
        @Override
        public void attributeRemoved(HttpSessionBindingEvent se) {
            System.out.println("属性移除");
        }
        // 属性被替换
        @Override
        public void attributeReplaced(HttpSessionBindingEvent se) {
            // 获取sesison对象
            HttpSession session = se.getSession();
            // 获取替换前的值
            Object old = se.getValue();
            System.out.println("原来的值:" + old);
            // 获取新值
            Object obj_new = session.getAttribute("userName");
            System.out.println("新值:" + obj_new);
        }
    }
    

    三、session相关监听器(HttpSessionBindingListener)
    - HttpSessionBindingListener : 监听对象绑定到session上的事件 (监听对象绑定/解除绑定到sesison上的事件! )
    1. 实现了HttpSessionBindingListener接口的 JavaBean 对象可以感知自己被绑定到 Session 中和从 Session 中删除的事件
    2. 当对象被绑定到 HttpSession 对象中时,web服务器调用该对象的 void valueBound(HttpSessionBindingEvent event) 方法
    3. 当对象从 HttpSession 对象中解除绑定时,web服务器调用该对象的 void valueUnbound(HttpSessionBindingEvent event) 方法
    4. 代码

    package anderson.sh.session;
    import javax.servlet.http.HttpSessionBindingEvent;
    import javax.servlet.http.HttpSessionBindingListener;
    // 监听此对象绑定到session上的过程,需要实现session特定接口
    public class Admin implements HttpSessionBindingListener {
      private int id;
      private String name;
      public Admin() {
          super();
      }
      public Admin(int id, String name) {
          super();
          this.id = id;
          this.name = name;
      }
      // 构造函数
      public int getId() {
          return id;
      }
      public void setId(int id) {
          this.id = id;
      }
      public String getName() {
          return name;
      }
      public void setName(String name) {
          this.name = name;
      }
      // 对象放入session
      @Override
      public void valueBound(HttpSessionBindingEvent event) {
          System.out.println("Admin对象已经放入session");
      }
      // 对象从session中移除
      @Override
      public void valueUnbound(HttpSessionBindingEvent event) {
          System.out.println("Admin对象从session中移除!");
      }
    }
    
    - HttpSessionActivationListener(了解) 监听session序列化及反序列化的事件
      1. 实现了HttpSessionActivationListener接口的 JavaBean 对象可以感知自己被活化和钝化的事件
      2. 当绑定到 HttpSession 对象中的对象将要随 HttpSession 对象被钝化之前,web 服务器调用如下方法 `` sessionWillPassivate(HttpSessionBindingEvent event) `` 方法
      3. 当绑定到 HttpSession 对象中的对象将要随 HttpSession 对象被活化之后,web 服务器调用该对象的 `` void sessionDidActive(HttpSessionBindingEvent event) ``方法
    
    • 生命周期监听器
      • 声明周期监听器: 监听对象的创建、销毁的过程!
        • 监听器开发步骤:
          1. 写一个普通java类,实现相关接口;
          2. 配置(web.xml)
    <!-- session的最大活跃时间 -->
        <session-config>
            <session-timeout>60</session-timeout>
        </session-config>
        <!-- 监听request对象创建、销毁 -->
        <listener>
            <listener-class>cn.itcast.a_life.MyRequestListener</listener-class>
        </listener>
        <!-- 监听session对象创建、销毁 -->
        <listener>
            <listener-class>cn.itcast.a_life.MySessionListener</listener-class>
        </listener>
        <!-- 监听servletContext对象创建、销毁 -->
        <listener>
            <listener-class>cn.itcast.a_life.MyServletContextListener</listener-class>
        </listener>
        <!-- 属性监听器 -->
        <listener>
            <listener-class>cn.itcast.b_attr.MySessionAttrListener</listener-class>
        </listener>
    
    • 404(路径写错); 500(服务器错误,调试)

    • 属性监听器

      • 监听:request/session/servletContext对象属性的变化!
        • ServletRequestAttributeListener
        • HttpSessionAttributeListener
        • ServletContextAttributeListener
    • 总结 : 先写类, 实现接口; 再配置

    • 其他监听器 : session相关监听器

      • 步骤 : 对象实现接口; 再把对象绑定/解除绑定到session上就会触发监听代码
      • 作用 : ( 上线提醒 )
      • 思考 : 这个session监听器,和上面的声明周期、属性监听器区别?
        • 不用再web.xml配置
        • 因为监听的对象是自己创建的对象,不是服务器对象!

    过滤器

    • 过滤器简介 :

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

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

      • Filter是如何实现拦截的?

        • Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
          • 调用目标资源前,让一段代码先执行
          • 是否调用目标资源(即是否让用户访问web资源)
            • web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。
          • 调用目标资源之后,让一段代码执行
      • 为什么需用到过滤器 : 项目开发中,经常会涉及到重复代码的实现!

      • 过滤器,设计执行流程:(OOAD : 面向对象的分析与设计 - 时序图)

        1. 用户访问服务器
        2. 过滤器: 对Servlet请求进行拦截
        3. 先进入过滤器, 过滤器处理
        4. 过滤器处理完后, 在放行, 此时,请求到达Servlet/JSP
        5. Servlet处理
        6. Servlet处理完后,再回到过滤器, 最后在由tomcat服务器相应用户;
      • 开发步骤 :

        • 写一个普通java类,实现Filter接口
        • 配置过滤器
    • 过滤器相关Api

      • interface Filter : 过滤器核心接口
        1. Void init(filterConfig); : 初始化方法,在服务器启动时候执行
        2. Void doFilter(request,response,filterChain); : 过滤器拦截的业务处理方法
        3. Void destroy(); : 销毁过滤器实例时候调用
      • interface FilterConfig : 获取初始化参数信息
      • interface FilterChain : 过滤器链参数;一个个过滤器形成一个执行链;
        1. void doFilter(ServletRequest request, ServletResponse response); : 执行下一个过滤器或放行
    package cn.itcast.a_filter_hello;
    import java.io.IOException;
    import java.util.Enumeration;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    // 过滤器,测试@author Jie.Yuan
    public class HelloFilter implements Filter{
        // 创建实例
        public HelloFilter(){
            System.out.println("1. 创建过滤器实例");
        }
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("2. 执行过滤器初始化方法");
            // 获取过滤器在web.xml中配置的初始化参数
            String encoding = filterConfig.getInitParameter("encoding");
            System.out.println(encoding);
            // 获取过滤器在web.xml中配置的初始化参数 的名称
            Enumeration<String> enums =  filterConfig.getInitParameterNames();
            while (enums.hasMoreElements()){
                // 获取所有参数名称:encoding、path
                String name = enums.nextElement();
                // 获取名称对应的值
                String value = filterConfig.getInitParameter(name);
                System.out.println(name + "\t" + value);
            }
        }
        // 过滤器业务处理方法: 在请求到达servlet之前先进入此方法处理公用的业务逻辑操作
        @Override
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            System.out.println("3. 执行过滤器业务处理方法");
            // 放行 (去到Servlet)
            // 如果有下一个过滤器,进入下一个过滤器,否则就执行访问servlet
            chain.doFilter(request, response);
            
            System.out.println("5. Servlet处理完成,又回到过滤器");
        }
        @Override
        public void destroy() {
            System.out.println("6. 销毁过滤器实例");
        }
    }
    
    • 对指定的请求拦截
      • 默认拦截的类型:(直接访问或者重定向 <dispatcher>REQUEST</dispatcher>
      • 拦截转发 : <dispatcher>FORWARD</dispatcher>
      • 拦截包含的页面(RequestDispatcher.include(/page.jsp); 对page.jsp也执行拦截<dispatcher>INCLUDE</dispatcher>
      • 拦截声明式异常信息 : <dispatcher>ERROR</dispatcher>
    • 共性问题:
      1. 过滤器 : 方法参数没有自动命名,说明没有关联源码-> 关联tomcat或servlet源代码
      2. 连接池 : 多刷新几次,报错!
        1. 连接没关 :
          QueryRunner qr = new QueryRunner();
          qr.update(con,sql);// 这里con一定要关闭
        2. 注意:dataSource 确定一个项目创建一次 : QueryRunner qr = new QueryRunner(dataSource);
          3.修改连接池参数配置
      3. 编码
      • request.setCharacterEncoding("UTF-8"); // 设置POST提交的请求的编码
      • response.setCharacterEncoding("UTF-8"); // 设置相应体的编码
      • response.setContentType("text/html;charset=UTF-8"); // 设置页面打开时候时候的编码格式、 设置相应体的编码
      1. 开发中 : 工作区间编码、项目编码、request/response、数据库编码一致

    过滤器案例

    • 过滤器-编码统一处理
      • 几乎每一个Servlet都要涉及编码处理:处理请求数据中文问题![GET/POST]
      • 每个servlet都要做这些操作,把公用的代码抽取-过滤器实现!
      • 代码实现思路:
        • Login.jsp : 登陆,输入“中文”
        • LoginServlet.java : 直接处理登陆请求
        • EncodingFilter.java : 过滤器处理请求数据编码:GET/POST
    // 过滤器 : 编码处理统一写到这里(servlet中不需要再处理编码)
    public class EncodingFilter implements Filter {
        // 过滤器业务处理方法:处理的公用的业务逻辑操作
        @Override
        public void doFilter(ServletRequest req, ServletResponse res,
                FilterChain chain) throws IOException, ServletException {
            // 转型
            final HttpServletRequest request = (HttpServletRequest) req;    
            HttpServletResponse response = (HttpServletResponse) res;
            
            // 一、处理公用业务
            request.setCharacterEncoding("UTF-8");                  // POST提交有效
            response.setContentType("text/html;charset=UTF-8");
            
            /*
             * 出现GET中文乱码,是因为在request.getParameter方法内部没有进行提交方式判断并处理。
             * String name = request.getParameter("userName");
             * 
             * 解决:对指定接口的某一个方法进行功能扩展,可以使用代理!
             *      对request对象(目标对象),创建代理对象!
             */
            HttpServletRequest proxy =  (HttpServletRequest) Proxy.newProxyInstance(
                    request.getClass().getClassLoader(),        // 指定当前使用的累加载器
                    new Class[]{HttpServletRequest.class},      // 对目标对象实现的接口类型
                    new InvocationHandler() {                   // 事件处理器
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args)
                                throws Throwable {
                            // 定义方法返回值
                            Object returnValue = null;
                            // 获取方法名
                            String methodName = method.getName();
                            // 判断:对getParameter方法进行GET提交中文处理
                            if ("getParameter".equals(methodName)) {
                                
                                // 获取请求数据值【 <input type="text" name="userName">】
                                String value = request.getParameter(args[0].toString());    // 调用目标对象的方法
                                
                                // 获取提交方式
                                String methodSubmit = request.getMethod(); // 直接调用目标对象的方法
                                
                                // 判断如果是GET提交,需要对数据进行处理  (POST提交已经处理过了)
                                if ("GET".equals(methodSubmit)) {
                                    if (value != null && !"".equals(value.trim())){
                                        // 处理GET中文
                                        value = new String(value.getBytes("ISO8859-1"),"UTF-8");
                                    }
                                } 
                                return value;
                            }else {
                                // 执行request对象的其他方法
                                returnValue = method.invoke(request, args);
                            }
                            return returnValue;
                        }
    });
                // 二、放行 (执行下一个过滤器或者servlet)
            chain.doFilter(proxy, response);  // 传入代理对象
        }
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {   
        }
        @Override
        public void destroy() {
        }
    }
    
    过滤器配置:
    <!-- 编码处理过滤器配置 -->
        <filter>
            <filter-name>encoding</filter-name>
            <filter-class>cn.itcast.a_loginFilter.EncodingFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>encoding</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
    public class LoginServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
    
            // 获取请求数据 
            String name = request.getParameter("userName");
            System.out.println("用户:" + name);
        }
    
    • 过滤器-无效数据过滤

      • 模拟:论坛过滤敏感词汇!
      • 实现思路:
        • Dis.jsp : 讨论区页面
        • DisServlet.java : 处理提交
          • 获取请求参数
          • 保存到request域
          • 跳转dis.jsp 【从request取数据显示(处理后)】
        • DataFilter.java : 过滤器
          • 编码
          • 无效数据处理
            即: 在上一个案例基础上,再添加无效数据过滤的相关代码!
    • JSP引入ckeditor组件:客户端组件,便于用户输入内容!

      • jsp :
    <!-- 引入ckeditor组件(给用户输入提供方便) --> 
        <script src="${pageContext.request.contextPath }/ckeditor/ckeditor.js"></script>
        <link rel="stylesheet" href="${pageContext.request.contextPath }/ckeditor/samples/sample.css">
    <body>
        ${requestScope.content }
        <form name="frmDis" action="${pageContext.request.contextPath }/dis" method="post">
          发表评论: <textarea class="ckeditor" rows="6" cols="30" name="content"></textarea>
          <br/>
          <input type="submit" value="评论" >
        </form>
      </body>
    
    • Filter:
    // 在上个过滤器案例的基础上,增加如下代码:
    // 中文数据已经处理完: 下面进行无效数据过滤   
    //【如何value中出现dirtyData中数据,用****替换】  
    for (String data : dirtyData) {
    // 判断当前输入数据(value), 是否包含无效数据
          if (value.contains(data)){
              value = value.replace(data, "*****");
          }
    }
    
    • 登陆权限判断

    • 登陆, 提交到登陆Servlet处理其业务!

      • 登陆成功, 跳转到首页,显示欢迎信息 + 列表信息
      • 登陆失败,跳转到登陆!
    • 要求:

      • 只有登陆后,才可以访问首页, 显示列表
      • 如果没有登陆,直接访问首页列表,要跳转到登陆!
    • 实现思路:

      • Login.jsp : 登陆页面
      • List.jsp : 列表显示
      • LoginServlet.java : 登陆处理servlet
      • IndexServlet.java : 首页列表查询Servlet
      • LoginFilter.java : 登陆验证过滤器
        (用之前的表 : admin存储登陆用户, 登陆用employee 存储员工信息,列表显示用!)
    • 实现步骤:

      1. 建库、建表、建项目、引入jar文件
      2. entity
      • Admin.java
      • Employee.java
      1. Dao
      • AdminDao
      • EmployeeDao
      1. Servcie
      2. Servlet
    • 注意 : 网站访问

    相关文章

      网友评论

        本文标题:监听器/过滤器(Servlet Listener / Filte

        本文链接:https://www.haomeiwen.com/subject/pdrbyttx.html