前言
前面对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就创建了,在服务器关闭时注销
它的生命周期可以从我们测试的方法中看出
- init初始化
如果在web.xml中注册了Filter,这个Filter将有服务器创建,并且会调用该init方法初始化(在ServletContext之后) - doFilter过滤
真正的过滤方法,程序员编写的业务 - 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配置的初始参数
有哪些参数呢?
- 类似与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容器(作用的范围)
- 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解决了客户端与服务器编码方式不同造成的乱码问题