servlet
是一种应用于服务端的,用于 Java Web 开发的,处理请求和响应输出的一种技术。
关于
Servlet 3.0
,可以先阅读 Java - servlet 3.0 这篇文章,了解相关知识,因为我们这边文章会用到Servlet 3.0
之后才提供的一些注解
Filter
(过滤器):是一段可复用的代码,卡在 请求/响应
与 servlet
之间,处理请求或者修改响应。
Filter
是 servlet
容器提供的功能,实现的接口是 javax.servlet.Filter
。Filter
的使用依赖于 servlet
容器(如 Tomcat
、JBoss
等),所以只能在 web
环境中使用。
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {
}
void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
default void destroy() {
}
}
接下来我们创建两个过滤器,来测试一下多个过滤器的执行顺序
创建一个编码过滤器 EncodingFilter
:
@WebFilter(filterName = "EncodingFilter", urlPatterns = "/*")
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init EncodingFilter...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 执行具体逻辑前
System.out.println("EncodingFilter before doFilter");
// 继续执行下一个过滤器
chain.doFilter(request, response);
// 执行具体逻辑后
System.out.println("EncodingFilter after doFilter");
}
@Override
public void destroy() {
System.out.println("destroy EncodingFilter...");
}
}
再创建一个记录请求参数和响应数据的过滤器 LoggerFilter
:
@WebFilter(filterName = "vloggerFilter", urlPatterns = "/*")
public class LoggerFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init loggerFilter...");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 执行具体逻辑前
System.out.println("loggerFilter before doFilter");
// 继续执行下一个过滤器
chain.doFilter(request, response);
// 执行具体逻辑后
System.out.println("loggerFilter after doFilter");
}
@Override
public void destroy() {
System.out.println("destroy loggerFilter...");
}
}
创建一个 HelloServlet
来处理请求:
@WebServlet(value = "/hello", initParams = {
@WebInitParam(name = "message", value = "hello world")
})
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("hello servlet 3.0");
}
}
启动 Servlet
容器,并请求 /hello
接口,输出结果:
// 启动 servlet 容器
init EncodingFilter...
init loggerFilter...
EncodingFilter before doFilter
loggerFilter before doFilter
loggerFilter after doFilter
EncodingFilter after doFilter
EncodingFilter before doFilter
loggerFilter before doFilter
loggerFilter after doFilter
EncodingFilter after doFilter
// 销毁 servlet 容器
destroy EncodingFilter...
destroy loggerFilter...
从上面的输出结果中我们可以看出:
Filter
只会初始化(init)一次Filter
的执行是有顺序的先进后出
的原则(如文章开头的图所示)因为涉及到多种注册方式,有
注解
、web.xml 配置
、运行时动态添加
等多种方式,所以关于Filter
的加载
和执行
顺序我们在之后的分析tomcat
源码的时候再讨论。
这里附上传送门:Tomcat - 分析 Servlet 中 Filter 的执行顺序
Filter
过滤器一般用于 设置字符编码
、URL级别的权限控制
、敏感词汇的过滤
、用户登录权限验证
等等,这些场景在 Spring 专栏 中关于 Spring
家族的框架源码学习中都会有具体的功能实现,比如 SpringCloudGateway 基于 Filter 实现的全局过滤器和局部过滤器
、SpringSecurity 基于 Filter 实现的认证与授权
、SpringBoot 基于 Filter 实现的全局字符编码过滤器
等等。
urlPatterns
参数中 /
和 /*
的区别(在 Servlet
规范的第 12 章中有介绍):
/
:会匹配到 /login
这样的 path mapping
,不会匹配到如 *.jsp
之类的 静态资源
/*
:会匹配到所有的资源,包括 path mapping
和 静态资源