真的了解Filter吗?- Filter详解

前言

前面对JavaWeb的一个个组件进行了了解
XML文件,Servlet组件、Listener组件、Cookie、Session
现在继续对JavaWeb的三组件:Filter进行复习总结

Filter的作用

在这里插入图片描述

画一个抽象的Web服务图,我们前面学习了组件Servlet和Listener

Servlet容器是具体处理各种业务的地方,例如Tomcat内置的JspServlet,我们自定义的业务Servlet等

Listener有8种,监听HttpRequest、ServletContext、HttpSession的生命周期与属性,监听Session的状态与绑定

那么今天学习的Filter过滤器,就是对Web服务器管理的所有资源的过滤拦截,通过Filter过滤器可以做到对请求与响应的过滤拦截,从而完成权限访问控制、过滤敏感词汇等功能

一个简单的Filter案例

Servlet API提供了一个Filter接口,实现这个接口的类即可以称为Filter过滤器,再注册到Web服务器就是一个过滤器了

写一个过滤器:功能是获得端口号

package filter;


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

public class FirstFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("FirstFilter过滤器创建了。。。");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        int port = servletRequest.getLocalPort();
        System.out.println("端口="+localAddr);
    }

    @Override
    public void destroy() {
        System.out.println("FirstFilter过滤器销毁了。。。");
    }
}

XML注册:拦截所有请求

	<filter>
        <filter-name>firstFilter</filter-name>
        <filter-class>filter.FirstFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>firstFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

猜猜会过滤几次:
在这里插入图片描述

请求、响应都会过滤一次
之所以4次是因为浏览器请求时还会请求图标
在这里插入图片描述

再刷新就是两次过滤了
在这里插入图片描述

这是一个很简单的案例,其中我们可以看到Filter有3个方法:init、doFilter、destroy,有4个参数:FilterConfig、ServletRequest、ServletResponse、FilterChain

它们的作用是什么?下面继续

生命周期

在这里插入图片描述

Filter接口很简单,就3个方法,定义了Filter的生命周期

从上面案例可以看出,Tomcat部署完成之前,Filter就创建了,在服务器关闭时注销

在这里插入图片描述

它的生命周期可以从我们测试的方法中看出

  1. init初始化
    如果在web.xml中注册了Filter,这个Filter将有服务器创建,并且会调用该init方法初始化(在ServletContext之后)
  2. doFilter过滤
    真正的过滤方法,程序员编写的业务
  3. destroy销毁
    服务器关闭时会调用该destroy方法销毁Filter并释放资源(在ServletContext之前)

注意:从生命周期可以看出,Filter过滤器是个单例模式,每一个过滤器会唯一存在Web服务器里

继承体系

在这里插入图片描述

继承体系:Filter - > GenericFilter - > HttpFilter

很眼熟,与Servlet类似:Servlet - > GenericServlet - > HttpServlet

那么看看内部的实现是否也是类似的:

GenericFilter :
在这里插入图片描述

这个类也简单,把FilterConfig聚合进了抽象类中,然后设置了一些获得FilterConfig属性的方法,简便了我们的操作(GenericServlet 也是这样的。。。)

在这里插入图片描述

那么HttpFilter呢?
在这里插入图片描述
仔细看看doFilter(ServletRequest request, ServletResponse response, FilterChain chain)方法体
也就是限制了传入的参数,必须要传入HttpServletRequest和HttpServletResponse

我们知道HttpServletRequest比ServletRequest 信息更多,还包含了一下HTTP协议的信息

那么对于Filter继承系统有了了解,Filter的继承体系与Servlet类似,下次使用Filter直接实现HttpFilter接口就行了

属性

看完了案例,发现3种方法中传递了4个参数:FilterConfig、ServletRequest、ServletResponse、FilterChain

其中ServletRequest、ServletResponse我们很了解,就是请求信息和响应信息,在HttpFilter中变成了更详细的参数HttpServletRequest和HttpServletResponse

着重了解FilterConfig和FilterChain

FilterConfig

从名字应该能了解到这是一个过滤器配置信息
我们前面学习Servlet的时候学到了个ServletConfig对象
真的了解Servlet吗? - Servlet的一些细节

那么这个FilterConfig对象是不是也和它类似呢?

首先找到这个接口
FilterConfig接口规定了4个方法
在这里插入图片描述

再看看它的继承体系:
在这里插入图片描述

到这里对FilterConfig应该有了一些猜想。。。

它就是获得在web.xml配置文件中Filter的信息的对象,并且可以得到ServletContext域对象,而ServletContext对象是上下文的对象,也是用来获取web.xml中的数据的对象

而GenericFilter和HttpFilter前面也看了,它是通过聚合的FilterConfig的方法实现的,绕来绕去,具体实现在Servlet包里是看不到的

具体的Filter对象的实现是通过服务器实现的,到底怎么实现的要深追到Tomcat源码,暂时没有这个实力

那么可以得出结论FilterConfig就是用来获得配置文件的属性的,它可以

getFilterName:获得web.xml的Filter的name

getInitParameter、getInitParameterNames获得部署配置文件(web.xml)中为Filter配置的过滤器初始化参数(初始化参数有挺多种的,后面详解)

getServletContext更强,直接得到ServletContext,把web服务器的上下文信息都能获取到

FilterChain

FilterChain也是个接口,也是由服务器实现封装好,我们只需要知道API即可(当然,如果深造的话需要深入Tomcat源码)

FilterChain接口就一个方法,doFilter业务方法
在这里插入图片描述
FilterChain接口的doFilter方法用于通知Web容器把请求交给Filter链中的下一个 Filter去处理,如果当前调用此方法的Filter对象是Filter链中的最后一个Filter,那么将把请求交给目标Servlet程序去处理

什么是Filter链?
我们知道每一个Filter都是单例模式,都是独一无二的,那么处理多个需求就需要多种Filter,那么它们运行顺序是什么?

很简单,就是根据在web.xml上注册映射的顺序
那么Filter链也可以理解了,如果一个Servlet有多个Filter,就根据这个映射顺序,将Filter的处理排成一个链,一个个处理,根据FilterChain接口的doFilter方法传递运行

所以需要注意:如果有一个Filter没有使用filterChain.doFilter,那么Filter链就会拦截在这,目标Servlet的service方法都不会被执行

ok,到这两个关键属性都有了了解,继续学习xml中Filter的属性

web.xml中Filter的配置

前面学习FilterConfig的getInitParameter方法说到能够得到为Filter配置的初始参数

有哪些参数呢?

  1. 类似与Servlet的配置信息
    <filter>
        <filter-name>firstFilter</filter-name>
        <filter-class>filter.FirstFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>firstFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <servlet-name>sessionTest</servlet-name>
    </filter-mapping>

filter的name、class
filter-mapping中url-pattern:过滤的地址 servlet-name:过滤的Servlet容器(作用的范围)

  1. dispatcher转发器的模式

在这里插入图片描述

具体有5种:
REQUEST :作用于直接从客户端过来的request
FORWARD:通过forward转发过来的request
INCLUDE:通过include过来的request
ERROR:通过<error-page>错误过来的request(HTTP请求响应的状态码是400、404、500)
ASYNC:异步拦截(在Servlet3.0版本以上可以异步拦截,用于处理异步Servlet,关于异步ASYNC挺复杂的,有空在深入)

默认是REQUEST,注意dispatcher要写在filter-mapping,毕竟dispatcher是依靠url-pattern

所以,我们通过过滤器设置编码时要注意转发器模式

解决中文乱码

写一个传递中文的Servlet

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

public class ServletCN extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();
        writer.write("这个一个中文语句,会不会乱码?");
    }
}

记得在web.xml中注册该Servlet

运行后,铁铁的乱码,乱码原因就是浏览器和服务器的编码方式不同

在这里插入图片描述

可以通过Filter设置编码,改变一个前面的FirstFilter

package filter;


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

public class FirstFilter implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("FirstFilter过滤器创建了。。。");
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //转换成HttpServletRequest才有设置编码的方法
        HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse)servletResponse;
        //获得web.xml中的编码方式
        String charset = filterConfig.getInitParameter("charset");
        if (charset == null){
            charset = "UTF-8";
        }
        //将请求和响应设置相同的编码
        httpServletRequest.setCharacterEncoding(charset);
        httpServletResponse.setCharacterEncoding(charset);
        //设置响应头信息
        httpServletResponse.setContentType("text/html;charset="+charset);

        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("FirstFilter过滤器销毁了。。。");
    }
}

测试
在这里插入图片描述

总结

这一节,复习了JavaWeb三大组件之一的Filter

了解了Filter的方法(生命周期):init初始化、doFilter业务方法、destroy销毁

Filter的继承体系:Filter - > GenericFilter - > HttpFilter ,类似与Servlet,其中的实现也是类似与Servlet的,仅仅做了一些方法的优化

Filter的两个关键的属性:FilterConfig:web.xml的Filter的相关配置属性
FilterChain:Filter链继续运行或者Servlet的service方法运行,需要使用FilterChain.doFilter方法传递HttpServeltRequest和HttpServletResponse

了解了Filter在web.xml中的配置属性,关键是dispatcher转发模式

并通过Filter解决了客户端与服务器编码方式不同造成的乱码问题

发布了109 篇原创文章 · 获赞 31 · 访问量 7371

猜你喜欢

转载自blog.csdn.net/key_768/article/details/105310004