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() { } }Servlet 环境中创建 Filter
接下来我们创建两个过滤器,来测试一下多个过滤器的执行顺序
创建一个编码过滤器 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 和 静态资源