Servlet过滤器

Servlet过滤器主要用于对客户端的请求进行过滤处理,将过滤后的请求转交给下一资源

什么是过滤器

Servlet过滤器与Servlet十分相似,但它具有拦截客户端请求的功能,Servlet过滤器可以改变请求中的内容来满足实际开发中的需要

对于程序开发人员来说,过滤器实际上就是在Web应用服务器上的一个Web应用组件,用于拦截客户端与目标资源的请求,并对这些请求进行一定过滤处理再发送给目标资源,同时,目标资源处理之后,请求的回应信息也同样需要经过过滤器
Servlet过滤器

如果一个Web应用中使用一个过滤器不能解决实际中的业务需求,可以部署多个过滤器对业务请求进行多次处理,这样就组成了一个过滤器链

过滤器核心对象

过滤器对象放置在javax.servlet包中,其名称为Filter,是一个接口。除此之外,与过滤器有关的对象还有FilterConfig对象(配置对象)与FilterChain对象(过滤器传递工具),这两个对象也是接口对象,位于同一个包中

在实际开发中,定义过滤器只需要直接或间接实现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接口
    FilterConfig接口由Servlet容器进行实现,主要用于获取过滤器中的配置信息,方法声明如下:
方法声明 说明
public String getFilterName() 用于获取过滤器的名字
public ServletContext getServletContext() 获取Servlet上下文
public String getInitParameter(String name) 获取过滤器的初始化参数
public Enumeration getInitParameterNames() 获取过滤器的所有初始化参数
  • FilterChain接口
    FilterChain接口仍由Servlet容器进行实现,这个接口中只有一个方法,如下:
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException

用于将过滤后的请求传递给下一个过滤器,如果是过滤链最后一个过滤器,则传递给目标资源

过滤器创建与配置

创建一个Filter过滤器需要实现javax.servlet.Filter接口的3个方法,例子如下:

import javax.servlet.*;
import java.io.IOException;

public class MyFilter implements Filter {
    public void init(FilterConfig config) throws ServletException{
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{
        // 在请求处理之后,需要调用chain参数的doFilter()将请求传给下一个过滤器或者目标资源
        chain.doFilter(request, response);
    }

    public void destroy(){
    }
}

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

过滤器与Servlet一样,在创建之后需要对其进行配置,分为两个步骤,主要为声明过滤器对象和创建过滤器映射

创建名为MyFilter的过滤器对象

<filter>
    <filter-name>MyFliter</filter-name>
    <filter-class>com.wang.yx.Filter.MyFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>MyFliter</filter-name>
    <url-pattern>/MyFilter</url-pattern>
</filter-mapping>

<filter>标签用于声明过滤器对象,这个标签中配置了两个子元素,为过滤器名称和过滤器完整类名

<filter-mapping>标签用于创建过滤器映射,它的作用就是指定Web应用中,哪些URL应用哪一个过滤器进行处理。其中<filter-name>用于定义过滤器的名称,<url-pattern>用于指定过滤器应用的URL

创建一个过滤器,实现网站访问计数器的功能,并在web.xml配置中将网站访问量的初始值设置为5000
(1)创建名称为CountFilter的类,通过该过滤器对象实现统计网站访问人数功能:

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class CountFilter implements Filter {
    private int count;
    @Override
    public void init(FilterConfig config) throws ServletException{
    	// 对count进行赋值,通过FilterConfig对象读取配置文件中的初始化参数进行获取
        String param = config.getInitParameter("count");
        count = Integer.valueOf(param);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException{
        count++;
        // 为了能够访问计数器中的值,将其放置于Servlet的上下文中,上下文对象通过将ServletRequest转换为HttpServletRequest对象后获取
        HttpServletRequest req = (HttpServletRequest)request;
        ServletContext context = req.getSession().getServletContext();
        context.setAttribute("count", count);
        chain.doFilter(request, response);
    }

    @Override
    public void destroy(){
    }
}

(2)配置已创建的CountFilter对象,通过配置web.xml文件实现:

    <filter>
        <filter-name>CountFilter</filter-name>
        <filter-class>com.wang.yx.Filter.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>

实例通过<init-param>标签配置过滤器的初始化参数,初始化参数为count,参数值为5000

(3)创建首页index.jsp,在页面中通过JSP内置对象Application获取计数器的值,如下:

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

字符编码过滤器
在Java Web开发中,由于Web容器内部所使用的编码格式不支持中文字符集,所以,处理浏览器请求中的中文数据就会出现乱码现象,如下图:
Servlet过滤器
解决此问题的方法,在业务处理中重新指定中文字符集进行编码即可解决。最简单的操作就是加入一个字符编码的过滤器,即使Web容器的编码格式不支持中文,但经过过滤器进行转码,所以可以避免中文乱码的产生,如图:
Servlet过滤器
eg:实现图书信息的添加功能,并创建字符编码过滤器,避免中文乱码现象的产生

(1)创建字符编码过滤器,名称为CharactorFilter类

import javax.servlet.*;
import java.io.IOException;

public class CharactorFilter implements Filter {
    String encoding = null;
    @Override
    public void destroy(){
        encoding = null;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (encoding != null){
            request.setCharacterEncoding(encoding);
            response.setContentType("text/html; charset=" + encoding);
        }
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig config)throws ServletException{
        encoding = config.getInitParameter("encoding");
    }
}

在创建了过滤器之后,还需要对过滤器进行一定的配置,修改web.xml,如下:

<filter>
    <filter-name>CharactorFilter</filter-name>
    <filter-class>com.wang.yx.Filter.CharactorFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharactorFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

实例中使用”/*“来匹配所有请求(可以用正则表达式来满足需要过滤器的请求)

(2)创建名为AddServlet的类,继承HttpServlet,是处理添加图书信息请求的Servlet对象,如下:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(name = "AddServlet")
public class AddServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PrintWriter writer = response.getWriter();
        String id = request.getParameter("id");
        String name = request.getParameter("name");
        String author = request.getParameter("author");
        String price = request.getParameter("price");
        writer.print("<h2>图书信息添加成功</h2><hr>");
        writer.print("图书编号:" + id + "<br>");
        writer.print("图书名称:" + name + "<br>");
        writer.print("作者:" + author + "<br>");
        writer.print("价格:" + price + "<br>");
        writer.flush();
        writer.close();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

在doGet()方法中调用doPost()方法,把业务处理代码写道doPost()方法中,或是相反,这样子无论Servlet接受的请求类型是GET还是POST,Servlet都会进行处理

编写Servlet类之后,也需要在web.xml中进行配置:

<servlet>
    <servlet-name>AddServlet</servlet-name>
    <servlet-class>com.wang.yx.servlet.AddServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>AddServlet</servlet-name>
    <url-pattern>/AddServlet</url-pattern>
</servlet-mapping>
<filter>
    <filter-name>MyFliter</filter-name>
    <filter-class>com.wang.yx.Filter.MyFilter</filter-class>
</filter>

(3)创建名为index.jsp页面,主要用于放置添加图书信息的表单,关键代码如下:

<body>
  <form action="AddServlet" method="post">
    <table align="center" border="1" width="350">
      <tr>
        <td class="2" align="center" colspan="2">
          <h2>添加图书信息</h2>
        </td>
      </tr>
      <tr>
        <td align="right">图书编号:</td>
        <td>
          <input type="text" name="id">
        </td>
      </tr>
      <tr>
        <td align="right">图书名称:</td>
        <td>
          <input type="text" name="name">
        </td>
      </tr>
      <tr>
        <td align="right">作    者:</td>
        <td>
          <input type="text" name="author">
        </td>
      </tr>
      <tr>
        <td align="right">价    格:</td>
        <td>
          <input type="text" name="price">
        </td>
      </tr>
      <tr>
        <td class="2" align="center" colspan="2">
          <input type="submit" value="添  加">
        </td>
      </tr>
    </table>
  </form>
</body>

运行实例,添加信息,即可查看运行结果。