Servlet过滤器简单探索
过滤器的工作时机介于浏览器和Servlet请求处理之间,可以拦截浏览器对Servlet的请求,也可以改变Servlet对浏览器的响应。
其工作模式大概是这样的:

一、Filter的原理
在Servlet API中,过滤器接口Filter会依赖于FilterChain和FilterConfig两个接口,都位于package javax.servlet;包中。
在Tomcat容器中,ApplicationFilterChain和ApplicationFilterConfig两个类分别实现了FilterChain和FilterConfig两个接口。
Filter接口和FilterChain接口都有一个叫doFilter()的方法,Filter的doFilter()在web应用中编写具体的过滤器时,由继承Filter的实现类来重写;FilterChain接口的doFilter()方法由Tomcat容器实现了。
相关接口和类的结构关系如下图:

以下简单探索Filter的原理。
ApplicationFilterChain类在Tomcat源码中位于包org.apache.catalina.core中。
用ApplicationFilterConfig类型的数组filters保存了web应用中设置的若干个ApplicationFilterConfig实例,由ApplicationFilterConfig实例可以获得web中定义的Filter实例。因此在这种意义上,ApplicationFilterConfig实例也就的映射成Filter实例。
pos指针记录filters数组中当前的ApplicationFilterConfig实例索引。
n指针存储所有的ApplicationFilterConfig实例数量。
在ApplicationFilterChain中,主要实现了FilterChain的doFilter()方法。
//package org.apache.catalina.core;
//继承javax.servlet.FilterChain(request,response)方法
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {//判断是否设置安全管理器
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedExceptionAction<Void>() {
@Override
public Void run()
throws ServletException, IOException {
internalDoFilter(req,res);//调用内部私有的internalDoFilter()方法
return null;
}
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
else if (e instanceof IOException)
throw (IOException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
throw new ServletException(e.getMessage(), e);
}
} else {
internalDoFilter(request,response);//调用内部私有的internalDoFilter()方法
}
}
内部私有的internalDoFilter()方法如下:
private void internalDoFilter(ServletRequest request,ServletResponse response)
throws IOException, ServletException {
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
//其他处理省略
if( Globals.IS_SECURITY_ENABLED ) {
//其他处理省略
//通过invoke()调用filter对象的doFilter()方法
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
//直接调用filter对象的doFilter()方法
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
//异常处理省略
}
return;
}
try {
//其他代码省略
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
//其他代码省略
//通过invoke()调用servlet对象的servicer()方法
SecurityUtil.doAsPrivilege("service",servlet,classTypeUsedInService,args,principal);
} else {
//直接调用servlet对象的service()方法
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
//异常处理省略
}
}
逻辑已经很清楚,当web应用设置了多了过滤器时(比如同时设置字符过滤器、请求参数编码过滤和静态资源过滤器三个过滤器)。会依次调用这三个Filter实例的doFilter方法,就相当于是一个任务链,完成之后,在调用servlet的service()方法正式处理请求逻辑。
总结起来,过滤器的工作流程大致如下:
请求到来,陆续调用各个Filter实例的doFilter()方法,然后在调用servlet的service()处理请求,之后在执行位于doFilter()方法后的代码处理逻辑,最后才返回给浏览器。
//service()前的处理逻辑
chain.doFilter(req, res);
//service()后的处理逻辑
二、简单应用
常见的设置过滤器的场景为:
1、特殊字符过滤器(如html页面元素:<,>等);
2、请求参数编码过滤器(GET请求参数和POST请求参数编码设置);
3、登录检测过滤器(判定用户是否登录)等等。
如下针对2和3的场景实现一个过滤器:
package com.tms.web; import java.io.IOException; import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; public class TMSFilter implements Filter {
private static Logger logger = Logger.getLogger(TMSFilter.class);
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest resquest, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest req = (HttpServletRequest)resquest;
HttpServletResponse res = (HttpServletResponse)response;
logger.info(req.getRequestURL());
//用户登录检测
if (req.getSession().getAttribute("hasLoginedUsername") != null) {
//请求参数编码设置
HttpServletRequest wreq = new TMSRequestWrapper(req,"UTF-8");
chain.doFilter(wreq, res);
//service()后的处理逻辑
}else {
res.sendRedirect("/tms/login");
}
}
}
通过继承HttpServletRequestWrapper类,覆盖其getParameter(String name)方法,将请求对象包装处理,使其针对GET和POST请求都能够正确的处理编码问题,这样的好处是集中在一个地方处理编码问题和登录检测,避免了在每个Servlet的doXXX()方法中去写这部分重复的代码,利于重构和维护,体现了DRY原则。
package com.tms.web; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper; public class TMSRequestWrapper extends HttpServletRequestWrapper {
private String encoding = "";
public TMSRequestWrapper(HttpServletRequest req,String encoding){
super(req);
this.encoding = encoding;
}
public String getParameter(String name){
HttpServletRequest req = (HttpServletRequest)getRequest();
String value = ""; try {
//POST请求编码设置,直接通过setCharacterEncoding()来设置编码方式
req.setCharacterEncoding(encoding);
value = req.getParameter(name);
//如果为GET请求,通过获取字节数据再转化为String对象来设置编码方式
if("GET".equals(req.getMethod())){
if (value != null) {
byte[] b = value.getBytes("ISO-8859-1");//ISO-8859-1
value = new String(b, encoding);
}
}
} catch (Exception e) {
// TODO: handle exception
new RuntimeException(e);
}
return value;
}
}
参考资料:
1、《深入分析java web技术内幕》
2、《Servlet和JSP笔记》
3、apache-tomcat-9.0.1-src
Servlet过滤器简单探索的更多相关文章
- Servlet 过滤器
一.过滤器介绍 在Servlet 2.3中定义了过滤器,它能够对Servlet容器的请求和响应进行检查和修改. Servlet过滤器能够在Servlet被调用之前检查Request对象,并修改Requ ...
- Servlet过滤器和监听器知识总结(转)
Servlet过滤器和监听器知识总结(转) Servlet过滤器是 Servlet 程序的一种特殊用法,主要用来完成一些通用的操作,如编码的过滤.判断用户的登录状态.过滤器使得Servlet开发者能 ...
- TODO java-web相关 servlet过滤器+监听器
servlet过滤器 定义: 过滤器是小型的web组件,它负责拦截请求和响应,以便查看.提供或以某种方式操作正在客户机和服务器之间交换的数据. 与过滤器相关的servlet共包含3个简单接口:Filt ...
- Servlet过滤器实现访客人数统计
第一. Servlet的创建和配置 1. 创建一个Servlet需要实现javax.servlet.Filter接口,同时实现Filter的3个方法. 第一个方法时过滤器中的 ...
- java基础篇---Servlet过滤器
Servlet过滤器从字面上的字意理解为景观一层次的过滤处理才达到使用的要求,而其实Servlet过滤器就是服务器与客户端请求与响应的中间层组件,在实际项目开发中Servlet过滤器主要用于对浏览器的 ...
- Servlet过滤器介绍之原理分析
zhangjunhd 的BLOG 写留言去学院学习发消息 加友情链接进家园 加好友 博客统计信息 51CTO博客之星 用户名:zhangjunhd 文章数:110 评论数:858 访问量:19 ...
- Servlet过滤器---简介
过滤器的基本概念 Servlet过滤器从字面上的字意理解为经过一层次的过滤处理才达到使用的要求,而其实Servlet过滤器就是服务器与客户端请求与响应的中间层组件,在实际项目开发中Servlet过滤器 ...
- Servlet过滤器的使用
Servlet过滤器的使用 制作人:全心全意 Servlet过滤器:Servlet过滤器与Servlet十分相似,但它具有拦截客户端请求的功能,Servlet过滤器可以改变请求中的内容,来满足实际开发 ...
- Servlet过滤器和监听器知识总结
Servlet过滤器是 Servlet 程序的一种特殊用法,主要用来完成一些通用的操作,如编码的过滤.判断用户的登录状态.过滤器使得Servlet开发者能够在客户端请求到达 Servlet资源之前被截 ...
随机推荐
- CDH5.11..0安装
1.参考: http://www.cnblogs.com/codedevelop/p/6762555.html grant all privileges on *.* to 'root'@'hostn ...
- poj2524 解题报告
基于并查集的一道简单题目 Ubiquitous Religions Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 22334 ...
- Failed to load the JNI shared lib...
启动eclipse报错:Failed to load the JNI shared lib... 解决办法如下:保证JDK与eclipse相匹配 在同一台计算机中,如果JDK是32位的,那么eclip ...
- .net程序员做的第一个安卓APP
我从毕业之后一直做的是ASP.NET,从毕业到现在都已经五六个年头了.一个事物接触久了就会慢慢没有了新鲜感热情也褪去的差不多了,我又是对新事物比较感兴趣的,虽说Android早就不算什么新事物,当对于 ...
- ChatterBot之快速入门01
本人运行环境为Python 3.5.2; 首先你需要导入chatterbot 的包,如果没有你先需要下载 使用命令 pip install chatterbot 1 # -*- coding: utf ...
- codeforces 897B Chtholly's request 偶数长度回文数
codeforces 897B Chtholly's request 题目链接: http://codeforces.com/problemset/problem/897/B 思路: 暴力求出这100 ...
- Undefined index: HTTP_RAW_POST_DATA的解决办法
$postStr = $GLOBALS["HTTP_RAW_POST_DATA"]; 替换为 $postStr = isset($GLOBALS['HTTP_RAW_POST_DA ...
- LinuxRPM包安装
转载注明出处:原文地址 ## LinuxRPM包安装 二进制包(RPM包.系统默认包) RPM安装 rpm -ivh 包全名(查询依赖网址:http://www.rpmfind.net) -i(ins ...
- EFCore2.0 Code First基本使用
前言 在网上找EFCore Code First相关的文章,很多都是基于core 1.0版本的,觉得有必要自己实践下2.0.所以,撸起袖子干吧!~ 1.新建控制台项目(这里就不放图了) 2.打开程序包 ...
- c语言中字符串函数的使用
#include<stdio.h> #include<string.h> /* char s1[]="I am a student"; char s2[20 ...