Servlet过滤器详解

日常生活里,自来水都是经过一层层的过滤处理才达到食用标准的,每一层过滤都起到一种净化的作用,今天所讲Servlet过滤器与自来水被过滤的原理相似
Servlet过滤器详解

1 定义

Servlet过滤器和 Servlet 很类似,注意的是它有拦截客户端请求的用法,Servlet过滤器可以改变请求中的内容,满足实际开发的需要。

开发来讲,过滤器实质上是在Web应用服务器里面的一个Web组件,目的用在拦截客户端(浏览器)与目标资源的请求,并把这些请求进行一定过滤处理在发给目标资源:
Servlet过滤器详解

看出,Web容器部署过滤器后,不仅客户端发送的请求会经过过滤器的处理,要不要返回呢?请求的回应信息也同样要经过过滤器。

如果一个Web应用中使用一个过滤器不能解决实际的业务需求,那可以部署多个过滤器对业务请求多次处理,这样做就组成一个过滤器链,Web容器在处理过滤器时,将被过滤器的先后顺序对请求进行处理:
Servlet过滤器详解

Web窗口中部署过滤器链,那么请求会一次按照过滤器顺序进行处理,在第一个过滤器处理一请求后,会传递给第二个过滤器进行处理,依次类推,传递到最后一个过滤器为止,再将请求交给目标资源进行处理,目标资源在处理经过过滤的请求后,其回应信息再从最后一个过滤器依次传递到第一个过滤器,最后传送到客户端;

2 过滤器核心对象

过滤器对象放置在 javax.servlet 包中,叫做 Filter, 是个接口,与过滤器相关的对象还有 FilterConfig对象 与 FilterChain对象,这两个也同样是接口对象,位于 javax.servlet 包中,分别作为过滤器的配置对象与过滤器的传递工具。在实际开发,定义过滤器对象只需要直接或间接地实现 Filter接口即可。下图的MyFilter1过滤器与MyFilter2过滤器,而 FilterConfig对象FilterChain对象 用在过滤器的相关操作:
Servlet过滤器详解

Filter接口

每个过滤器对象只直接或间接地实现 Filter接口,在Filter接口中定义了3个方法,分别是 init(),doFilter()和destroy(),方法及说明如表:

方法 声明
public void init(FilterConfig filterConfig) throws ServletException 过滤器初始化方法,该方法在过滤器初始化时调用
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException 对请求进行过滤处理
public void destroy() 销毁方法,以便释放资源

FilterConfig接口

Servlet容器进行实现,主要是获取过滤器中的配置信息,其方明及说明如表:

方法 说明
public String getFilterName() 用于获取过滤器的名字
public ServletContext getServletContext() 获取Servlet上下文
public String getInitParameter(String name) 获取过滤器的初始化参数值
public Enumeration getInitParameterNames() 获取过滤器的所有初始化参数

FilterChain接口

它由Servlet容器进行实现,在这个接口中一个方法,其方法声明:
public void doFilter(ServletRequest request, ServletResponse response)throws IOException,ServletException 该方法将过滤的请求传给下一个过滤器,如果此过滤器已经是过滤器链中的最后一个过滤器,那么,请求传送给目标资源。

过滤器创建于配置

创建一个过滤器对象需要实现 javax.serjavax.servlet.Filter接口,下面演示过滤器的创建:

创建名称为 MyFilter的过滤器对象,其代码如下:

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servletFilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
* 过滤器
*/
public class MyFilter implements Filter{
       //初始化方法
       public void init(FilterConfig fConfig) throws ServletException{
       //初始化处理
       }
       //过滤处理方法
       public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletEXception {
       //过滤处理
       chain.doFilter(request, response);
       }
       //销毁方法
       public void destroy(){
       //释放资源
       }     
}

过滤器的init()对过滤器的初始化进行处理,destroy()是过滤器的销毁方法,主要是释放资源,对于过滤器处理的业务需要编写到doFilter()里面,在请求过滤需调用chain参数的doFilter()将请求向下传给下一过滤器或目标资源:

使用过滤器并不一定要将请求向下传递到下一过滤器或目标资源,如果业务逻辑需要,也可以在过滤处理后,直接回应客户端;

过滤器与Servlet十分相似,在创建后同样对其配置,过滤器的配置主要分为两个步骤,分别在声明过滤器对象和创建过滤器映射,创建名为MyFilter的过滤器对象:

<!- 过滤器声明 ->
<filter>
      <!- 过滤器的名称 ->
      <filter-name>MyFilter</filter-name>
      <!- 过滤器的完整类名 ->
      <filter-class>com.lyq.Myilter</filter-class>
</filter>
<!- 过滤器映射 ->
<filter-mapping>
    <!- 过滤器名称 ->
    <filter-name>MyFilter</filter-name>
    <!-- 过滤器 URL映射 ->
    <url-pattern>/MyFilter</filter-name>
    <!- 过滤器URL映射 ->
    <url-pattern>/MyFilter</url-pattern>
</filter-mapping>    
      

< filter>标签用在声明过滤器对象,这个标签中必须配置两个子元素,分别是过滤器的名称过滤器完整类名,其中< filter-name>定义过滤器的名称,< filter-class>指定过滤器的完整类名。

< filter-mapping>标签用于创建过滤器的映射,它的主要作用是指定Web应用中,哪些URL应用哪一个过滤器进行处理,在< filter-mapping>标签里面需要你自己指定过滤器应用的URL。

< filter>标签中的< filter-name>可以是自定义的名称,而< filter-mapping>标签中的< filter-name>是指定已定义的过滤器名称,没法改变。它需要和< filter>标签中的< filter-name>一一对应;

Servlet过滤器详解

我们试着创建一个过滤器,完成网站访问计数器的功能,在web.xml文件的配置里,将网站访问量的初始值设置为5000。

新建名称是CountFilter的类,该类实现 javax.servlet.Filter接口,它是个过滤器对象,通过该过滤器实现统计网站访问人数,关键代码:

  
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;  
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
  
public class CountFilter implements Filter{
  
  //来访数量
  private int count;
  @Override
  public void init(FilterConfig filterConfig) throws ServletException{
              String param = filterConfig.getInitParameter("count");
        count++;
    
  //将ServletRequest 转换成 HttpServletRequest
  HttpServletRequest req = (HttpServletRequest)request;
  
  //获取ServletContext
  ServletContext context = req.getSession.getServletContext();
  context.setAttribute("count", count);
  chain.doFilter(request, response);
}
  
  @Override
  public void destroy(){
  
  }
}
  

在ComFiler类中,包含一个成员变量 count,用于记录网站访问人数,该变量在过滤器的初始化方法init() 中被赋值,它的初始值通过FilterConfig对象读取配置文件中的初始化参数就可以得到;

计数器 count变量的值在CountFilter类的 doFilter()中递增, 因为客户端在请求服务器中的Web应用时侯,过滤器拦截请求通过 doFilter()方法进行过滤处理,所以当client请求Web应用时,计数器 ount的值将加1, 为了能够访问计数器中的值,实例中我们把它放置在 Servlet 上下文中,Servlet 上下文对象通过将 ServletRequest 转换为 HttpServletRequest对象后获取。

写过滤器对象需要我们实现javax.servlet.Filter接口,实现该接口后需要对 Filter对象的3个方法进行实现,在这3个方法中,除了 doFilter()方法外,如果在业务逻辑中不涉及初始化方法 init()与销毁方法 destroy(), 可以不编写任何代码对其进行空实现,如实例中的 destroy()方法。

配置已创建的 CountFilter对象,此操作通过配置web.xml文件进行实现。

<filter>  
  <filter-name>CountFilter</filter-name>
  <filter-class>com.lyq.CountFilter</filter-class>
  <init-param>
        <param-name>count</param-name>
        <param-value>5000</param-value>
    </init-param>
</filter>
        
  <filter-mapping>
        <filter-name>CountFilter</filter-name>
         <url-pattern>/index.jsp</url-pattern>
  </filter-mapping> 
  

CountFilter对象的配置主要通过声明过滤器及创建过滤器的映射进行实现,其中声明过滤器要通过 < filter>标签进行实现。在声明过程中,实例用< init-param>标签配置过滤器的初始化参数,初始化参数的名称为count,参数值为5000;

如果直接对过滤器对象的成员变量进行赋值,那在过滤器被编译后将不可修改,所以,实例中将过滤器对象中的成员变量定义为过滤器的初始化参数,提高了代码的灵活性;

创建程序中的首页index.jsp页面,在该页面中通过JSP内置对象Application获取计数器的值。关键代码如下:

  
<body>
    <h2>
      欢迎光临, <br>
      你是本站的第【
      <%=application.getAttribute("count") %>
        】位访客!
        </h2>
</body>
  

因为在web.xml中将计数器的初始值设置为 5000,所以实例运行后,计数器的数值变为大于5000的数,在多次刷新页面后,实例运行结果:

上一篇:FastAPI(11)- 函数参数类型是列表,但不使用 typing 中的 List,而使用 list,会怎么样?


下一篇:代理服务器(Proxy)原理