七、Filter和Listener

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中进行过滤器的配置

    注册过滤

    配置会进行拦截的地址

      
      
      
         filterDemo
         com.sxt.filter.FilterDemo
      
      
      
         filterDemo
         
         /*
      
    
过滤器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() {
        
    }

}

    
        charsetEncodingFilter
        com.sxt.filter.CharsetEncodingFilter
        
        
            charset
            ISO-8859-1
        
    
    
    
        charsetEncodingFilter
        
        /*
    

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() {
        
    }

}


    
        loginFilter
        com.sxt.filter.LoginFilter
    
    
        loginFilter
        /*
    

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);
    }
    

}

    
    com.sxt.listener.RequestListener
  

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());
    }
}


    com.sxt.listener.SessionListener
  

注意:

​ 当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("删除了属性");
    }   

}

  
    com.sxt.listener.ApplicationListener
  

注意:

  1. tomcat可以通过配置发布非tomcat服务器中webapps中的项目

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

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

2.4.ServletContext的初始化参数配置

  
  
    name1
    value1
  
  
    name2
    value2
  
        //获取到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)