Java Web之使用过滤器

Java Web之使用过滤器
write:2022-4-22

前文我们学习了JSP页面引用Java Bean:Java Web之JSP使用Java Bean,这节课我们学习Java Web的过滤器。
在实际应用中,每个web组件都需要检查客户请求的代码,如果每个web组件都编写请求的代码,显然重复编码降低开发效率和可维护性,为了解决这个问题,过滤器产生,过滤器是Servlet规范2.3才产生的技术。
过滤器能够对一部分客户请求进行预处理操作,然后再把请求转发给实际上对应的web组件,对于生成的响应结果,过滤器还能进行检查和修改,再把修改后的结果响应给客户。

1. 过滤器的概念

过滤器是在Java Servlet规范2.3中定义的,它能够对客户请求和web组件生成的响应对象进行检查和修改(预处理)。
过滤器本身并不生成请求和响应对象,它只提供过滤作用。
过滤器能够在Web组件被调用之前检查Request对象,修改Request Header和Request内容;
在Web组件被调用之后检查Response对象,修改Response Header和Response内容。
过滤器可以为Servlet、JSP或HTML文件等Web组件提供过滤。

2. 过滤器的工作过程

在这里插入图片描述

3. 创建过滤器的方法

3.1 Filter接口

所有的过滤器类都必须实现javax.servlet.Filter接口。这个接口含有3个过滤器类必须实现的方法,三个方法分别在过滤器不同生命周期被调用:
init()
doFilter()
destroy()

(1)init(FilterConfig):这是过滤器的初始化方法,Servlet容器创建过滤器实例后将调用这个方法。在这个方法中可以读取web.xml 文件中过滤器的初始化参数

(2)doFilter(ServletRequest, ServletResponse,FilterChain):
这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL时,Servlet容器将先调用过滤器的doFilter方法。FilterChain参数用于访问后续过滤器或者web组件;上面方法的三个参数是由Servlet容器传的

(3)destroy():Servlet容器在销毁过滤器实例调用该方法,在这个方法中可以释放过滤器占用的资源

3.2 过滤器的例子

下面创建一个NoteFilter过滤器,它可以拒绝列在黑名单上的客户访问留言簿,而且能将Web组件响应客户请求所花的时间写入日志。

3.2.1 NoteFilter.java

假定客户真正请求的web组件是NoteServlet,而NoteServlet前面有一个NoteFilter过滤器:

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

public class NoteFilter implements Filter {
    private FilterConfig config = null;
    private String blackList=null;

    public void init(FilterConfig config) throws ServletException {
      this.config = config;
      blackList=config.getInitParameter("blacklist");
    }

    public void destroy() {
      config = null;
    }

    public void doFilter(ServletRequest request, ServletResponse response,
                     FilterChain chain) throws IOException, ServletException {

        String username =((HttpServletRequest) request).getParameter("username");
	if (username!=null )username=new String(username.getBytes("ISO-8859-1"),"GB2312");
       if (username!=null && username.indexOf(blackList) != -1  )  {
            response.setContentType("text/html;charset=GB2312");
            PrintWriter out = response.getWriter();
            out.println("<html><head></head><body>");
            out.println("<h1>对不起,"+username + ",你没有权限留言 </h1>");
            out.println("</body></html>");
            out.flush();
            return;
       }

      long before = System.currentTimeMillis();
      config.getServletContext().log("NoteFilter:before call chain.doFilter()");
      chain.doFilter(request, response);
      config.getServletContext().log("NoteFilter:after call chain.doFilter()");
      long after = System.currentTimeMillis();
      String name = "";
      if (request instanceof HttpServletRequest) {
        name = ((HttpServletRequest)request).getRequestURI();
      }
      config.getServletContext().log("NoteFilter:"+name + ": " + (after - before) + "ms");
    }
}

代码流程分析:
当NoteFilter初始化时,调用init()方法,有一个FilterConfig参数,它包含了配置信息,而这些配置信息一开始存放在web.xml文件中,它调用config.getInitParameter(“blacklist”)方法,从web.xml文件中读取初始化参数blacklist,这个参数表示被禁止访问留言簿的客户黑名单。
在这里插入图片描述
在NoteFilter的doFilter()方法中首先从request对象中读取客户姓名,然后将客户姓名转换为中文字符编码。如果客户姓名中包含黑名单里的字符串,那么将直接向客户端返回一个拒绝网页。
在这里插入图片描述
由于在这种情况下没有调用chain.doFilter()方法,因此客户请求不会到达客户请求真正所访问的Web组件。

所以根据条件就有过滤器拒绝请求转发请求两种情况:

3.2.2 NoteFilter过滤器拒绝HTTP请求

假定姓名中包含“捣蛋鬼”字符串的客户将被禁止访问留言簿,并且留言簿由NoteServlet类来实现,当名叫“捣蛋鬼2000”的客户访问留言簿时,将被NoteFilter过滤器拒绝访问。返回结果:
在这里插入图片描述
当名叫“捣蛋鬼2000”的客户访问留言簿时NoteFilter的工作流程:
“捣蛋鬼2000”的客户请求的web组件是NoteServlet,但有NoteFilter过滤器在,根本没有到NoteServlet,返回结果的拒绝网页也是有NoteFilter生成的;
在这里插入图片描述

3.2.3 NoteFilter过滤器转发HTTP请求

当不在黑名单中的客户请求访问,调用doFilter()方法,在此方法中,又会调用chain.doFilter()方法:

long before = System.currentTimeMillis();//调用chain.doFilter()方法前的时间
config.getServletContext().log(
                  "NoteFilter:before call chain.doFilter()");
chain.doFilter(request, response);//如果当前过滤器与后续过滤器有关联,就转发给后续过滤器,如果没有就把请求转发给后续的web组件
config.getServletContext().log(
                  "NoteFilter:after call chain.doFilter()");
long after = System.currentTimeMillis();//调用chain.doFilter()方法后的时间
String name = "";
if (request instanceof HttpServletRequest) {
        name = ((HttpServletRequest)request).getRequestURI();
}
config.getServletContext().log("NoteFilter:"+name + ": " + (after - before) + "ms");//相减计算出NoteServlet响应客户请求所花的时间

当名叫“小精灵”的客户访问留言簿时,将被NoteFilter过滤器转发请求访问。返回结果:
在这里插入图片描述
计算调用chain.doFilter()方法之前和之后的时间:
在这里插入图片描述
当名叫“小精灵”的客户访问留言簿时NoteFilter的工作流程:
在这里插入图片描述

4. 发布过滤器的方法

4.1 配置过滤器

(1)发布过滤器时,必须在web.xml文件中加入< filter>元素和< filter-mapping>元素。< filter>元素用来定义过滤器,< filter-mapping>元素用于将过滤器和URL映射:

<filter-mapping> 
    <filter-name>NoteFilter</filter-name> 
    <url-pattern>/note</url-pattern> 
</filter-mapping>

在上面的例子中:

<filter> 
    <filter-name>NoteFilter</filter-name> 
     <filter-class>mypack.NoteFilter</filter-class> 
	<init-param>  //设置参数
          <param-name>blacklist</param-name>
    	   <param-value>捣蛋鬼</param-value>
 	</init-param>
 </filter>

通过config.getInitParameter(“blacklist”)就可以得到参数值;
(2)< filter-mapping>元素用于将过滤器和URL映射,如果希望过滤器能为所有的URL过滤,那么可以把< url-pattern>的值设为“/* ”。这样,当客户请求访问Web应用中的任何一个Web组件时,Servlet容器都会先把请求交给过滤器处理;
(3)在web.xml文件中,必须先配置过滤器,再配置Servlet;

4.2 创建NoteServlet类

创建一个Servlet类NoteServlet,它实现一个简单的留言簿。它提供了一个表单,让客户输入姓名和留言,客户提交表单后,再将用户输入的信息显示在客户端的网页上。
NoteServlet.java:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class NoteServlet extends HttpServlet {
  private static final String CONTENT_TYPE = "text/html;charset=GB2312";

  public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType(CONTENT_TYPE);
    PrintWriter out = response.getWriter();
    out.println("<html>");
    out.println("<head><title>留言薄</title></head>");
    out.println("<body>");

    String username=request.getParameter("username");
    String content=request.getParameter("content");
    if(username!=null)username=new String(username.getBytes("ISO-8859-1"),"GB2312");
    if(content!=null)content=new String(content.getBytes("ISO-8859-1"),"GB2312");

    if(content!=null && !content.equals(""))
      out.println("<p>"+username+"的留言为:"+content+"</P>");

    out.println(" <FORM  action="+request.getContextPath()+"/note method=POST>");

    out.println("<b>姓名:</b>");
    out.println("<input type=text size=10 name=username ><br>");
    out.println("<b>留言:</b><br>");
    out.println("<textarea name=content rows=5 cols=20  wrap></textarea><br>");
    out.println("<BR>");
    out.println("<input type=submit  value=提交>");
    out.println("</form>");
    out.println("</body></html>");
  }

  public void destroy() {
  }
}

NoteServlet的配置代码:

<servlet>
        <servlet-name>NoteServlet</servlet-name>
        <servlet-class>mypack.NoteServlet</servlet-class>
</servlet>

<servlet-mapping>
        <servlet-name>NoteServlet</servlet-name>
        <url-pattern>/note</url-pattern>//映射路径与NoteFilter一致
</servlet-mapping>

访问NoteServlet:
http://localhost:8080/应用名/note(先处理NoteFilter再处理NoteServlet)

5. 多个过滤器协同工作

(1)串联过滤器的工作流程
过滤器1和过滤器2都为Servlet工作,先调用过滤器1再调用过滤器2
在这里插入图片描述
(2)配置串联的过滤器
因为是先调用过滤器1再调用过滤器2,所以先配置过滤器1再配置过滤器2

<filter> 
    <filter-name>Filter1</filter-name> 
    <filter-class>Filter1</filter-class> 
</filter> 
<filter-mapping> 
    <filter-name>Filter1</filter-name> 
    <url-pattern>/note</url-pattern> 
</filter-mapping> 


<filter> 
    <filter-name>Filter2</filter-name> 
    <filter-class>Filter2</filter-class> 
</filter> 
<filter-mapping> 
    <filter-name>Filter2</filter-name> 
    <url-pattern>/note</url-pattern> 
</filter-mapping> 

6. 练习题

  1. 问题:在过滤器中能否访问application范围内的共享数据?
    答案:可以的,先调用FilterConfig的getServletContext()方法获得ServletContext,再调用ServletContext的getAttribute()方法来获得application范围内的共享数据。
  2. 问题:“过滤器只能对Servlet进行过滤”,这句话是否正确?
    答案:不正确。过滤器可以对Servlet、JSP和HTML文件过滤。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值