美文网首页
七、Filter和Listener

七、Filter和Listener

作者: 艾特小师叔 | 来源:发表于2020-03-13 23:09 被阅读0次

    Servlet进阶

    1.过滤器-Filter

    1.1.什么过滤器?

    过滤器,即具有拦截过滤的作用的器具。例如:筛子,渔网等等,都是拦截器。

    而在JavaWeb程序中,可以将WEB中的请求进行拦截过滤的程序就被称之为过滤器.

    1.2.为什么要存在过滤器?

    因为服务器是对外开放,所有的客户端都可以进行访问。但是在访问中,存在非法的或无效的等类型的访问。但是,访问一段传递给了程序,程序就会进行处理。就会进行运算,这样会导致,服务器因此增大服务器压力,也可能大致服务器运算异常。所以,需要对WEB的请求,进行过滤筛选,若是不合法的请求,进行特殊的处理。

    1.3.如何使用过滤器?

    在JavaWeb中,规定了接口Filter,只要实现了Filter接口就是一个过滤器,然后注册到服务器,且配置要进行过滤的地址。

    1. 创建类实现Filter接口 重写相关方法

      package com.sxt.filter;
      
      import java.io.IOException;
      
      import javax.servlet.Filter;
      import javax.servlet.FilterChain;
      import javax.servlet.FilterConfig;
      import javax.servlet.ServletException;
      import javax.servlet.ServletRequest;
      import javax.servlet.ServletResponse;
      
      /**
       * @ClassName: FilterDemo 
       * @Description: 过滤器  filter 示例
       * @author: Mr.T
       * @date: 2020年2月16日 下午3:52:57
       */
      public class FilterDemo implements Filter {
       /**
        *  当Filter 初始化调用的方法
        */
       @Override
       public void init(FilterConfig filterConfig) throws ServletException {
           System.out.println("============init===========");
       }
       /**
        *  具体进行过滤的方法
        *      当使用过滤器链对象,将请求对象和响应对象向下传递  则表示放行
        *  request : 请求对象
        *  response : 响应对象
        *  chain : 过滤器链
        */
       @Override
       public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
               throws IOException, ServletException {
           System.out.println("==============doFilter===============");
           
           System.out.println("放行前!!!");
           //进行放行
           chain.doFilter(request, response);
           System.out.println("放行后!!!");
       }
       /**
        *  当服务器停止后  销毁对象时 调用的方法
        */
       @Override
       public void destroy() {
           System.out.println("============destroy==============");
       }
      
      }
      
      
    2. 在web.xml中进行过滤器的配置

      注册过滤

      配置会进行拦截的地址

        <!-- 配置过滤器 -->
        <!-- 注册过滤器 -->
        <filter>
           <filter-name>filterDemo</filter-name>
           <filter-class>com.sxt.filter.FilterDemo</filter-class>
        </filter>
        <!-- 配置要进行过滤的地址  -->
        <filter-mapping>
           <filter-name>filterDemo</filter-name>
           <!-- 如果当前访问的URL  符合下面配置的规则,则会进行过滤 -->
           <url-pattern>/*</url-pattern><!-- 所有的地址都会进行过滤 -->
        </filter-mapping>
      
    过滤器1.png

    1.4.过滤器的生命周期

    在web程序中,过滤器在服务器启动时,会默认初始化,创建过滤器对象,调用init方法进行初始化。

    当有符合过滤规则的请求,就会调用doFilter方法,进行过滤。

    当服务器,停止时,会调用destroy方法,释放内存。

    注意:

    ​ init 和 destroy 方法都只会执行一次

    1.5.过滤器链

    如果存在多个过滤器,且一次访问也符合多个过滤条件,那么相关的过滤器都会执行。

    且先执行的过滤器后执行执行完。在web.xml的配置中,自上而下的过滤器,配置在上面的优先执行。

    过滤器链.png

    1.6.过滤器使用场景

    过滤器在开发中很常用,例如:

    1.编码过滤器,利用过滤器的特性,会在具体的程序执行前执行。可以在执行前设置编码,且会在具体的程序执行完后执行,可以在执行完后设置响应数据的编码。

    2.登录过滤器,在实际项目中,有些功能,需要用户登录登录后才能进行相关操作。所以,可以检测URL,从而判断是否是需要登录的,若是需要登录的,则获取当前登录用户,若存在,则放行,不存在则跳转到登录页面,让用户登录。

    3.数据采集。利用过滤对符合条件的URL都能过滤的特性。可以将同类的URL进行过滤,如:查询的URL(*query.do)的URL,可以获取所以查询参数,可以将查询参数进行保存。

    4.权限过滤器。利用过滤的特性,检查请求的URL,根据当前用户的角色,判断用户是否存在当前权限,若存在放行,若不存在则提示没有权限访问。

    1.7.过滤器案例

    1.7.1.编码过滤器

    编码过滤器即将请求和响应的编码设置为需要的编码

    package com.sxt.filter;
    
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    
    /**
     * @ClassName: CharsetEncodingFilter 
     * @Description: 编码过滤器  
     * @author: Mr.T
     * @date: 2020年2月16日 下午4:55:07
     */
    public class CharsetEncodingFilter  implements Filter{
        //设置过滤器的默认编码
        String charset = "UTF-8";
        
        /**
         *  init 方法 是初始化方法 会优先于  doFilter
         *  可以初始化一些成员变量
         */
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            //过滤器配置信息
            //获取配置的编码
            String initCharset= filterConfig.getInitParameter("charset");
            if(initCharset != null && initCharset !="") {
                //将配置文件中编码 覆盖掉默认编码
                charset = initCharset;
            }
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            System.out.println("此时的编码为:"+charset);
            //设置编码
            request.setCharacterEncoding(charset);
            response.setCharacterEncoding(charset);
            //放行请求
            chain.doFilter(request, response);
        }
    
        @Override
        public void destroy() {
            
        }
    
    }
    
    
        <filter>
            <filter-name>charsetEncodingFilter</filter-name>
            <filter-class>com.sxt.filter.CharsetEncodingFilter</filter-class>
            <!-- 过滤器初始化参数 -->
            <init-param>
                <param-name>charset</param-name>
                <param-value>ISO-8859-1</param-value>
            </init-param>
        </filter>
        <!-- 配置要进行过滤的地址  -->
        <filter-mapping>
            <filter-name>charsetEncodingFilter</filter-name>
            <!-- 如果当前访问的URL  符合下面配置的规则,则会进行过滤 -->
            <url-pattern>/*</url-pattern><!-- 所有的地址都会进行过滤 -->
        </filter-mapping>
    

    1.7.2.登录过滤器

    一般用户登录,会放入session中。‘

    登录过滤器,需要根据rquest对象,拿到用户放问的URL,判断该URL是否是需要登录的URL,若是需要登录的URL,则检测是否已经登录,若没有登录则跳转到登录页面,若已登录则放行。

    以stmng为例

    目前用户没有登录,可以访问任何地址。限制: 若用户没有登录,则用户只能访问login.jsp.

    package com.sxt.filter;
    
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    /**
     * @ClassName: LoginFilter 
     * @Description: 登录过滤器
     *   需要过滤的: 除不需要过滤的都要进行过滤
     *  不需要过滤的: 登录页面 、js文件、css文件、图片文件等静态资源文件、验证码请求      
     * 
     * 
     * @author: Mr.T
     * @date: 2020年2月16日 下午5:12:06
     */
    public class LoginFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse resp = (HttpServletResponse)response;
            //1. 获取请求的URI
            //获取请求的资源路径
            String uri = req.getRequestURI();
            System.out.println("请求的资源路径为: "+uri);
            //2. 获取当前的登录用户   
            Object user = req.getSession().getAttribute("user");
            //2.1 如果用户已经登录  则直接放行
            if(user != null) {
                System.out.println("用户已经登录放行");
                chain.doFilter(req, resp);
                return;
            }
            //2.2 如果用户没有登录,则判断是否是不需要过滤的请求,是则放行 不是则跳转到登录页面
            //不需要过滤的: 登录页面 、js文件、css文件、图片文件等静态资源文件、验证码请求    
            System.out.println();
            //如果是以 login.jsp 结尾 放行
            //如果是js文件、css文件、图片文件等静态资源文件 则判断是否是  resources开头
            //要根据  /项目/resources 开头   stmng
            System.out.println("项目根目录:"+req.getContextPath());
            //静态资源的访问目录
            String staticPath = req.getContextPath() +"/resources";
            //登录页面  和 静态资源文件目录   验证码  放行
            if(uri.endsWith("login.jsp") || uri.startsWith(staticPath) || uri.endsWith("checkCode.do")) {
                chain.doFilter(req, resp);
                return;
            }
            //具体的登录请求 和 注册请求也要放行
            String service = req.getParameter("service");
            if(uri.endsWith("user.do") && ("login".equals(service)||"register".equals(service))) {
                chain.doFilter(req, resp);
                return;
            }
            //其他情况  跳转到 登录页面
            resp.sendRedirect("login.jsp");
            
        }
    
        @Override
        public void destroy() {
            
        }
    
    }
    
    
    <!-- 配置过滤器 -->
        <filter>
            <filter-name>loginFilter</filter-name>
            <filter-class>com.sxt.filter.LoginFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>loginFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    

    2.监听器-Listener

    2.1.什么是监听器?

    监听器就是起到监视侦听的器具,可以随时监视事物的变化。例如:古代战争中用于监视敌人是否袭来的城墙边上的瓮,烽火台。而在JavaWeb中的监听器,是用于监视WEB中的作用域状态的程序。主要监视,作用域的声明周期以及属性的变化。

    在JavaWeb中,Servlet有3大作用域:

    HttpServletRequest request

    HttpSession session

    ServletContext application

    注意:

    ​ jsp由于其特殊性,存在9大内置对象,4大作用域,其中pageContext是其特有的,其他3个作用域,与Servlet的作用域是一致。

    而WEB中监听器注意监听的是3个对象的声明周期以及属性的变化。基于这点,WEB提供了2类接口:

    监听声明周期的接口:

    HttpServletRequest的生命周期监视器接口:ServletRequestListener

    HttpSession的生命周期监视器接口:HttpSessionListener

    ServletContext的生命周期监视器接口: ServletContextListener

    监听属性变化的接口:

    HttpServletRequest的属性监视器接口:ServletRequestAttributeListener

    HttpSession的属性监视器接口:HttpSessionAttributeListener

    ServletContext的属性监视器接口: ServletContextAttributeListener

    2.2.为什么会存在监听器?

    监视对象的生命周期,即监视对象的创建和销毁。监视对象的属性变化,即监视对象的活动。根据对象的声明和销毁,对象的属性发生改变从而调用相应的方法,可以在不改变原有编码的基础上,干预程序的运算。

    2.3.如何使用监听器?

    监听器的使用相对简单,因为每个监听器只有固有的作用,如果想监听对象的生命周期,则实现相应的生命周期的接口,如果想监听属性的变化,则实现监听属性变化的接口。在配置文件注册监听器。

    HttpServletRequest的监听器

    package com.sxt.listener;
    
    import java.io.UnsupportedEncodingException;
    
    import javax.servlet.ServletRequestAttributeEvent;
    import javax.servlet.ServletRequestAttributeListener;
    import javax.servlet.ServletRequestEvent;
    import javax.servlet.ServletRequestListener;
    import javax.servlet.http.HttpServletRequest;
    /**
     * @ClassName: RequestListener 
     * @Description: HttpServletRequest的生命周期和属性监听器
     *              ServletRequestListener    生命周期监听器
     *                  requestInitialized : 当有request对象被实例化时会调用
     *                  requestDestroyed : 当有request 对象被销毁时调用
     * 
     *              现在RequestListener  实现了接口  就是遵循监听器的规范。RequestListener此时
     *              只是一个符合监听器规范的程序。还需要配置  监听器才能真正生效
     * 
     * @author: Mr.T
     * @date: 2020年2月17日 上午9:49:53
     */
    public class RequestListener implements ServletRequestListener,ServletRequestAttributeListener {
        
        /**
         * @Title: requestInitialized
         * @author: Mr.T   
         * @date: 2020年2月17日 上午9:51:44 
         * @Description: 当存在request 对象被实例化时 会被调用
         * @param sre
         * @return: void
         */
        @Override
        public void requestInitialized(ServletRequestEvent sre) {
            System.out.println("request 对象 被创建了");
            //拿到当前已经创建的request 对象
            HttpServletRequest req = (HttpServletRequest) sre.getServletRequest();
        }
        
        /**
         * @Title: requestDestroyed
         * @author: Mr.T   
         * @date: 2020年2月17日 上午9:52:28 
         * @Description: 当request 对象 被销毁时 调用
         * @param sre
         * @return: void
         */
        @Override
        public void requestDestroyed(ServletRequestEvent sre) {
            System.out.println("request 对象 被销毁了");
            
            
        }
        /**
         *  当作用域中有新增的属性会触发该方法
         */
        @Override
        public void attributeAdded(ServletRequestAttributeEvent srae) {
            System.out.println("有新增的属性了");
            String name = srae.getName();
            Object value = srae.getValue();
            System.out.println("新增的属性的name值:"+name);
            System.out.println("新增属性的value值:"+value);
            System.out.println("发生属性变化的对象:"+srae.getServletRequest());
            if(name.equals("sex")) {
                String sex = value.equals("1")?"男":"女";
                //此处修改了属性值
                srae.getServletRequest().setAttribute(name, sex);
            }
            
        }
        /**
         *  当作用域中发生属性修改会触发该方法
         */
        @Override
        public void attributeReplaced(ServletRequestAttributeEvent srae) {
            System.out.println("当属性值发生修改时触发");
            // 被修改的数据的name值
            String name =  srae.getName();
            //被修改的数据的之前的值
            Object value = srae.getValue();
            //被修改的数据的新值
            System.out.println(srae.getServletRequest().getAttribute(name));
            System.out.println("attributeReplaced中的name:"+ name);
            System.out.println("attributeReplaced中的value:"+ value);
        }
        /**
         *  当作用域中发生属性的删除会触发该方法
         */
        @Override
        public void attributeRemoved(ServletRequestAttributeEvent srae) {
            System.out.println("当属性中发生删除时触发");
            String name = srae.getName();
            Object value = srae.getValue();
            System.out.println(name+":"+value);
        }
        
    
    }
    
    
        <listener>
        <listener-class>com.sxt.listener.RequestListener</listener-class>
      </listener>
    

    Session的监听器

    package com.sxt.listener;
    
    import javax.servlet.http.HttpSession;
    import javax.servlet.http.HttpSessionAttributeListener;
    import javax.servlet.http.HttpSessionBindingEvent;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;
    /**
     * @ClassName: SessionListener 
     * @Description: session的监听器
     *      HttpSessionListener : session 生命周期监听器
     *          sessionCreated : 当session创建时触发
     *          sessionDestroyed : 当session销毁时触发
     *      HttpSessionAttributeListener : session的属性监听器
     *          
     * 
     * 
     * 
     * @author: Mr.T
     * @date: 2020年2月17日 上午10:47:55
     */
    public class SessionListener implements HttpSessionListener,HttpSessionAttributeListener {
        /**
         *  Session创建时触发
         */
        @Override
        public void sessionCreated(HttpSessionEvent se) {
            //注意:每次会话 执行一次
            System.out.println("session创建了");
        }
        /**
         *  session 销毁时触发
         */
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
    //      System.out.println("session 销毁时触发");
    //      //当session 与request的绑定关系   此时session 等待被gc回收
    //      //则调用该方法
    //      HttpSession session = se.getSession();
    //      //获取到这个被等待回收的session
    //      System.out.println(session);
    //      //从session 中获取属性值
    //      System.out.println(session.getAttribute("me"));
        }
        @Override
        public void attributeAdded(HttpSessionBindingEvent se) {
            System.out.println("当session新增属性时触发");
        }
        
        @Override
        public void attributeReplaced(HttpSessionBindingEvent se) {
            System.out.println("当session中 发生属性修改时触发");
        }
        
        @Override
        public void attributeRemoved(HttpSessionBindingEvent se) {
            System.out.println("当session中发生属性删除时触发");
            //被删除的数据的 name值
            System.out.println("删除中的name:"+se.getName());
            //被删除数据的value值
            System.out.println("删除中的value:"+se.getValue());
        }
    }
    
    
    <listener>
        <listener-class>com.sxt.listener.SessionListener</listener-class>
      </listener>
    

    注意:

    ​ 当session调用销毁方法时,还会调用删除属性方法,session会自动的将其中所有属性进行删除,则会触发多次

    attributeRemoved方法。

    ServletContext常用功能介绍

    package com.sxt.controller;
    
    import java.io.IOException;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    @WebServlet("/test3.do")
    public class TestController3 extends HttpServlet {
    
        private static final long serialVersionUID = -5123674255870472885L;
    
        @Override
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //ServletContext 是一个全局容器  整个应用程序  有且只有一个  
            ServletContext context = req.getServletContext();
            //getRealPath("")
            //getRealPath("") : 获取项目在服务器中WebRoot目录的绝对路径
            //getRealPath 是获取项目的WebRoot 或者WebRoot指定的资源的绝对路径
            System.out.println(context.getRealPath("img"));
            //当使用文件操作时,涉及到流数据操作,必须使用绝对路径,所以getRealPath一般用于文件上传下载 
            //上传  需要将文件保存在一个物理磁盘中 需要使用绝对路径
            //下载  需要从一个物理磁盘 将文件变为流数据传给浏览器  也需要绝对路径
            //getContextPath(): 获取的项目访问名称
            System.out.println(context.getContextPath());
            String rootPath = context.getContextPath();
            //在WEB开发中,一般URL路径,使用绝对路径: 协议:地址:端口/资源路径 这样的形式。
            //在WEB开发中,浏览器中的URL地址是变化的。相对路径是相对当前路径,当前URL路径
            //容易出现路径错误,如果使用绝对路径,则没有该问题
            //绝对路径  适用于url 路径是任何情况
            resp.sendRedirect(rootPath+"/index.jsp");       
        }
    
        
    }
    
    

    ServletContext的监听器

    package com.sxt.listener;
    
    
    import javax.servlet.ServletContextAttributeEvent;
    import javax.servlet.ServletContextAttributeListener;
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    
    /**
     * @ClassName: ApplicationListener 
     * @Description: ServletContext 监听器
     *              ServletContextListener : ServletContext 生命周期监听器
     *                  contextInitialized : 当ServletContext对象被实例化时调用 只会调用一次
     *                                          当服务器启动时就会调用
     *                  contextDestroyed  : 当ServletContext 对象被销毁时调用  也只会调用一次
     *                                          当服务器停止时  调用
     *              ServletContextAttributeListener :ServletContext 属性监听器
     * 
     * @author: Mr.T
     * @date: 2020年2月17日 下午2:15:36
     */
    public class ApplicationListener implements ServletContextListener,ServletContextAttributeListener {
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            System.out.println("ServletContext 实例化");
            
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
            System.out.println("ServletContext 销毁");
        }
        /**
         *  当ServletContext中新增属性时 调用
         */
        @Override
        public void attributeAdded(ServletContextAttributeEvent scae) {
            System.out.println("新增属性");
        }
        /**
         *  当发生属性修改时调用
         */
        @Override
        public void attributeReplaced(ServletContextAttributeEvent scae) {
            System.out.println("修改了属性");
        }
        /**
         *  当发生属性删除时调用
         */
        @Override
        public void attributeRemoved(ServletContextAttributeEvent scae) {
            System.out.println("删除了属性");
        }   
    
    }
    
    
      <listener>
        <listener-class>com.sxt.listener.ApplicationListener</listener-class>
      </listener>
    

    注意:

    1. tomcat可以通过配置发布非tomcat服务器中webapps中的项目
    <Context docBase="F:\10listener" path="/listener" reloadable="true" />
    
    1. 由于ServletContext的特殊性,ServletContext是整个WEB项目中最先被实例化的对象,且只会被实例化一次,全局都共享。基于这点,ServletContext的生命周期方法最常使用,特别是初始方法.因为可以在项目启动时就将一些配置信息通过该方法进行读取,配置。这样,实际使用时只需要进行调用即可。虽然程序启动相对变慢,但是实际使用时提高了效率。例如:

      1. JDBC的连接,在创建连接时需要读取配置文件中配置信息,加载驱动。然后再创建连接。可以在ServletContext初始化方法中,直接读取配置文件,加载驱动,创建一个连接池。这样在真正需要使用连接的地方只需要调用即可。
      2. 在WEB开发,每层可能会其他层调用。则需要类对象,可以在实例化时,创建类对象,需要使用的地方,只需要获取类对象,然后调用方法。不需要自己去创建。

    2.4.ServletContext的初始化参数配置

      <!-- 配置ServletContext的初始化参数 -->
      <context-param>
        <param-name>name1</param-name>
        <param-value>value1</param-value>
      </context-param>
      <context-param>
        <param-name>name2</param-name>
        <param-value>value2</param-value>
      </context-param>
    
            //获取到ServletContext
            ServletContext context = sce.getServletContext();
            //根据参数的key  获取对应的值
            String initParameter1 = context.getInitParameter("name1");
            System.out.println(initParameter1);
            String initParameter2 = context.getInitParameter("name2");
            System.out.println(initParameter2);
    

    相关文章

      网友评论

          本文标题:七、Filter和Listener

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