博主信息:
📢@博主: 嘟嘟的程序员铲屎官
💬:一位爱喵咪,爱开源,爱总结,爱分享技术的Java领域新星博主,如果你想和博主做朋友,关注博主,并私聊博主(给我发一条消息我就会关注你喔),博主本人也十分喜欢解决问题,如果你有什么问题,也可以来私聊博主喔,希望能够和C站的朋友相互学习,相互进步。
💬:关于本篇博客,最近在看廖雪峰大佬JavaWeb部分的知识,对大佬的 使用Filter这一篇进行个人总结,如果有什么错误的,请各位大佬能够及时提出,以免小弟误人子弟!
@Toc
一.使用Filter
1.Filter
为了把一些公用逻辑从各个Servlet中抽离出来,JavaEE的Servlet规范还提供了一种Filter组件,即过滤器,它的作用是,在HTTP请求到达Servlet之前,可以被一个或多个Filter预处理,类似打印日志、登录检查等逻辑,完全可以放到Filter中。
2.Filter处理方式
Filter的处理方式如下图:
- 客户端的请求会经过过滤器,然后到达目标资源进行处理,目标资源处理之后再经过过滤器最后响应给客户端。
多个Filter组成一条Filter链如下图:
- 在第1个过滤器处理一个请求后,会传递给第2个过滤器处理,直到最后一个过滤器将请求交给目标资源处理。目标资源在处理经过过滤的请求后,其响应信息从最后一个过滤器依次传递到第1个过滤器,最后传送到客户端。
3.Filter的核心对象
下面通过2个操作,来理解Filter的核心对象:
操作1:
在Tomcat中lib目录下,将servlet-api.jar
拷贝到任意一个文件下并进行解压
会生成如下二个文件:
javax:
源码文件META-INF文件:
用于存储包和扩展的配置数据,如安全性和版本信息
进入..\javax\servlet
从上面的操作我们可知:
Filter位于javax.servlet
包下, Filter相关的几个.class
文件如下:
- Filter.class
- FilterChain.class
- FilterConfig.class
- FilterRegistration$Dynamic.class
- FilterRegistration.class
操作2:
通过jad将.class
反编译为.java
文件
相关资料:
jad:jad.exe(java反编译工具)32/64位
教程: 如何将class文件转换成java文件
操作非常简单,windows+R输入cmd,进入dos按照下图所示进行操作即可
我的jad目录如下:
进入到反编译存放文件中,我们会发现Filter相关的.java
只有如下四个:
- Filter.java
- FilterChain.java
- FilterConfig.java
- FilterRegistration.java
打开Filter.java,对该类进行分析
从上面的源码可知:
Filter是一个接口,该接口有如下三个抽象方法(方法的解释来源于<<JavaWeb入门经典>>这本书):
Filter接口抽象方法 | 方法解释 | |
---|---|---|
init(FilterConfig filterconfig) | 过滤器初始化方法,此方法在初始化过滤器时调用 | |
doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain filterchain) | 对请求进行过滤处理 | |
destroy(); | 销毁方法以释放资源 |
打开FilterConfig.java,对该类进行分析
从上面的源码可知:
FilterConfig是一个接口,用于配置Filter,该接口有如下四个抽象方法(方法的解释来源于<<JavaWeb入门经典>>这本书)
FilterConfig接口抽象方法 | 方法解释 | |
---|---|---|
getFilterName(); | 用于获取过滤器名 | |
getServletContext(); | 获取Servlet上下文 | |
getInitParameter(String s); | 获取过滤器的初始化参数值 | |
getInitParameterNames(); | 获取过滤器的所有初始化参数 |
FilterConfig的配置可通过标签的方式进行配置:
@WebFilter(urlPatterns={"/index","/sign"})
该标签对访问/index
和/sign
路径的请求将经过该过滤器,注意urlPatterns={"/*"
}标识对所有请求都要经过该过滤器。
FilterConfig的配置可通过web.xml的方式进行配置(和Servlet类似):
如果多个过滤器应用于同一个资源,那么调用顺序就很重要,必须用部署描述符管理应该先调用哪一个过滤器。假如Filter1必须在Filter2之前调用,那么在部署描述符中,Filter1的声明就要放在 Filter2的声明之前。
打开FilterChain.java文件,对该类进行分析
从上面的源码可知:
FilterChain是一个接口,该接口只有如下一个抽象方法
public abstract void doFilter(ServletRequest servletrequest, ServletResponse servletresponse)
throws IOException, ServletException;
此方法用于将过滤后的请求传递给下一个过滤器,如果此过滤器是过滤器链中的最后一个过滤器,那么请求将传送给目标资源。
打开FilterRegistration.java,对该类进行分析
从上面的源码可知:
FilterRegistration是一个接口,接口中有一个嵌套接口和4个抽象方法
嵌套接口:
public static interface Dynamic extends FilterRegistration,Registration.Dynamic
{
}
通过该接口可以进一步配置Filter,通过ServletContext上的addFilter方法注册Filter。
4个抽象方法:
FilterRegistration接口抽象方法 | 方法解释 | |
---|---|---|
addMappingForServletNames(EnumSet enumset, boolean flag, String as[]); | 通过ServletNames添加的映射 | |
getServletNameMappings(); | 获取Servlet映射名称集合 | |
addMappingForUrlPatterns(EnumSet enumset, boolean flag, String as[]); | 通过UrlPatterns添加映射 | |
getUrlPatternMappings(); | 获取当前可用URL模式映射。 |
总结: 从上面的分析我们可以知道,Filter是一个接口,该接口下有三个抽象方法init()用于初始化Filter,doFilter()用于处理过滤的业务逻辑,destroy()用于销毁Filter,然后就是与Filter相关的二个接口,FilterConfig接口用于Filter的配置,FilterChain接口用于对Filter链进行管理,通过doFilter()方法将请求向下传递给下一个过滤器或目标资源,另外还有一个FilterRegistration接口用于对Filter进一步进行配置。
4.Filter例子(字符编码过滤器)
在前面我们每编写一个Servlet都会在相应的doGet()/doPost()方法中写如下代码用于避免出现乱码,显然该代码是一个公用的代码,对于这种代码我们可以将它放到Filter中的doFilter()中进行处理,这样就避免了重复编写此代码。
项目结构:
IndexServlet.java
SignInServlet.java
index.html
运行效果:
点击跳转到IndexServlet
点击跳转到SignServlet
解决方法:
在该项目中创建一个filter的包,在该包下创建一个EncodingFilter.java类该类实现Filter接口,并重写该接口的三个抽象方法,通过@WebFilter标签
初始化FilterConfig,再将IndexServlet和SignInServlet的公用代码移到该类的doFilter()中,然后通过FilterChain.doFilter()方法传递到下一个Filter或目标资源。
再次运行效果一致,但是Servlet中少了公用代码:
IndexServlet.java