天天看点

Servlet 过滤器 (第六篇)

文章目录

    • 6.1、什么是过滤器?
      • 6.1.1、过滤器适用场景
    • 6.2、主要概念
      • 6.2.1、过滤器的生命周期
      • 6.2.2、包装请求和响应
      • 6.2.3、过滤器环境
      • 6.2.4、在web应用配置过滤器
      • 6.2.5、过滤器和请求转发器(RequestDispatcher)
body和header数据进行过滤和修改,对静态数据和动态数据过滤,具体的语法可以参考14章

6.1、什么是过滤器?

  • 过滤器是一段可以重用的代码,它能够拦截Http请求和响应,同时header和body做一些修改适配,这个有点类似流水线,过滤器是就工人
  • 可以多个过滤器完成过滤的功能,过滤器链

6.1.1、过滤器适用场景

  • 认证过滤
  • 日志和审计过滤
  • 图片转换过滤
  • 数据压缩过滤
  • 加密过滤
  • Tokening 过滤
  • 访问事件触发过滤器
  • XSL/T 过滤器和转换XML内容
  • MIME-type 过滤链
  • 缓存过滤器

6.2、主要概念

  • 实现自定义过滤器,需要实现javax.servlet.Filter接口,提供无参构造方法,在web应用配置描述文件(web.xml)中配置( 和), 表示你自定义过滤器的类路径, filter-mapping 拦截资源的url 匹配

6.2.1、过滤器的生命周期

  • 在使用过滤功能之前,首先需要实例化过滤器,就需要调用init(FilterConfig config)方法,这个方法可能会抛出异常,如果抛出是UnavailableException异常,那么容器会调用isPermanent属性判断是否需要重试
  • 在单个JVM中有且仅有每个对应一个实例,也就单例
  • 当一个请求过来之后,他们都会调用doFilter方法(组成一个过滤器链)
  • doFilter方法典型实现场景
    1. 判断request请求的头部信息
    2. 可能会包装自定义实现ServletRequest或HttpServletRequest对象,或者修改头部和主体
    3. 可能会包装自定义实现ServletResponse或HttpServletResponse对象,或者修改头部和主体
    4. doFilter可能调用下一个也是过滤器,也就另一个过滤器doFilter方法,这个就形成过滤器链(FilterChain),过滤器链如果没有调用下一个实体(资源或其他过滤器)的话可能会阻塞请求,对应过滤器链运行在同一个线程中
    5. 调用下一个过滤器,可能会验证以响应的头
    6. 执行过滤器链中可能会抛出异常(UnavailableException) 容器会根据isPermanent来判断是否需要随后重试,还直接终止请求
    7. 过滤器链最后被调用可能是目标Servlet或资源
    8. 在过滤实例化之前,servlet容器可以通过destroy方法去移除这个过滤器

6.2.2、包装请求和响应

  • 支持改变请求或响应对象的内容

6.2.3、过滤器环境

  • 通过 初始化参数, 在运行时候通过FilterConfig类的getInitParameter和getInitParameterNames方法来获取这些初始化参数,同时FilterConfig会存储ServletContext上下文的状态

6.2.4、在web应用配置过滤器

  • 除了使用在部署配置web.xml文件中加入 配置过滤器,还可以通过@WebFilter来配置过滤器
  • @WebFilter具有参数
    • filter-name: 使用过滤器映射到一个servlet或URL
    • filter-class: 让容器识别过滤器的类型
    • init-params; 为这个过滤器初始化参数
  • 例子
    • <!--首先定义一个filter过滤器-->
      <filter>
      	<filter-name> Image Filter</filter-name>
        <filter-class>com.acme.ImageServlet</filter-class>
      </filter>
      
      <!--定义过滤器映射到servlet上, 是通过filter-name进行关联的-->
      <filter-mapping>
      	<filter-name>Image Filter</filter-name>
        <servlet-name>ImageServlet</servlet-name>
      </filter-mapping>
      
      <!--定义一个日志过滤器去匹配和拦截所有的URL-->
      <filter-mapping>
      	<filter-name>Logging Filter</filter-name>
        <url-pattern>/*</url-pattern>
      </filter-mapping>
      
      <!--如果一个过滤器同时匹配url和servlet-->
      <filter-mapping>
        <filter-name>Multipe Mapping Filter</filter-name>
        <url-pattern>/foo/*</url-pattern>
        <servlet-name>Servlet1</servlet-name>
        <servlet-name>Servlet2</servlet-name>
        <url-pattern>/bar/*</url-pattern>
      </filter-mapping>
      
      <!--等同于下面-->
      <filter-mapping>
      <filter-name>Multipe Mappings Filter</filter-name> 
        	<url-pattern>/foo/*</url-pattern>
      </filter-mapping>
      <filter-mapping>
      <filter-name>Multipe Mappings Filter</filter-name>
        <servlet-name>Servlet1</servlet-name>
      </filter-mapping>
      <filter-mapping>
      <filter-name>Multipe Mappings Filter</filter-name> 
        <servlet-name>Servlet2</servlet-name>
      </filter-mapping>
      <filter-mapping>
      <filter-name>Multipe Mappings Filter</filter-name> 
        <url-pattern>/bar/*</url-pattern>
      </filter-mapping>
                 
    • 对于构建过滤链,可以缓存,提高web的性能

6.2.5、过滤器和请求转发器(RequestDispatcher)

  • 从2.4的Servlet版本中 forward和include可以配置filters(过滤器链),进行一波重定向或其他操作都是可能匹配 从而经过过滤器拦截。
  • 一些属性
    • REQUEST
    • FORWARD
    • INCLUDE
    • ERROR
    • ASYNC
  • 例子
    • <!--配置一个日志的过滤器映射, 主要是/products开头的url-->
      <filter-mapping>
      	<filter-name>Logging Filter</filter-name>
        <url-pattern>/products/*</url-pattern>
      </filter-mapping>
      
      <!--客户请求不会直接调用,只有发生了include,才会匹配到日志过滤器-->
      <filter-mapping>
      	<filter-name>Logging Filter</filter-name>
        <servlet-name>ProductServlet</servlet-name>
        <dispatcher>INCLUDE</dispatcher>
      </filter-mapping>
      
      <!--客户请求的url是/products开头,同时调用forward或request方法才会匹配到日志过滤器-->
      <filter-mapping>
      	<filter-name>Logging Filter</filter-name>
        <url-pattern>/products/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
      </filter-mapping>
      
      <!--所有servlet只要是调用了forward方法都会匹配到All Dispatch Filter过滤器-->
      <filter-mapping>
      	<filter-name>All Dispatch Filter</filter-name>
        <servlet-name>*</servlet-name>
        <dispatcher>FORWARD</dispatcher>
      </filter-mapping>