一文搞懂过滤器和拦截器

什么是 Servlet

想要深刻理解两者的区别,需要有一个知识储备,对 servlet 和 Spring MVC 的理解。

简单的说,Spring MVC 是对 servlet 的封装和解藕。

广义上的 servlet 是 Java 中用于支持处理浏览器的请求,并生成响应的一组类和接口,位于 javax.servlet.* 包下。

狭义上的 servlet 是继承 HttpServlet 的类,并重写 doGet()、doPost() 等方法来处理浏览器的请求并生成响应的 Java 对象,常用的 Tomcat 服务器用来管理 Servelt 的生命周期,称为 Servlet 容器。一般来说,一个功能模块对应一个 servlet ,对于同一个路径下,不同子路径的 get 请求,可以在 doGet 方法的进行条件判断。例如:

xml 复制代码
<!-- web.xml 配置 -->
<servlet>
  <servlet-name>UserServlet</servlet-name>
  <servlet-class>com.example.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>UserServlet</servlet-name>
  <url-pattern>/user/*</url-pattern>
</servlet-mapping>

<servlet>
  <servlet-name>OrderServlet</servlet-name>
  <servlet-class>com.example.OrderServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>OrderServlet</servlet-name>
  <url-pattern>/order/*</url-pattern>
</servlet-mapping>
scala 复制代码
public class UserServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        if ("GET".equals(req.getMethod())) {
            doGet(req, resp);
        } else if ("POST".equals(req.getMethod())) {
            doPost(req, resp);
        }
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        String action = req.getParameter("action");
        if ("list".equals(action)) {
            // 处理用户列表请求
        } else if ("detail".equals(action)) {
            // 处理用户详情请求
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        String operation = req.getParameter("operation");
        if ("create".equals(operation)) {
            // 处理用户创建请求
        } else if ("update".equals(operation)) {
            // 处理用户更新请求
        }
    }
}

从使用示例中,我们可以很清晰的看出 Servlet 编程的优点,1、职责分离:不同模块的 Servlet 独立维护,代码边界清晰。2、配置简单:通过简单的配置就可以让 Servlet 对象与 URL 绑定。

同时,缺点也不言而喻,1、代码冗余:每个 Servlet 模块需要重复编写请求解析、响应处理等公共逻辑。2、扩展性差:每当新增加功能都需要增加一个 Servlet ,同时修改配置文件,可能会导致配置文件的臃肿。3、路由不灵活:无法通过统一匹配逻辑,需要手动解析路径参数。

什么是过滤器

在Servlet中,过滤器是指实现 javax.servlet.Filter 接口,实现 init 、doFilter、destory 方法的类。对客户端请求和响应进行预处理和后处理的组件,在请求到达 Servlet 之前对请求进行过滤和修改,在响应返回给客户端之前对响应进行处理,例如:

typescript 复制代码
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>com.example.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>


import javax.servlet.*;
import java.io.IOException;

public class CharacterEncodingFilter implements Filter {
    private String encoding;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 从过滤器配置中获取字符编码参数
        encoding = filterConfig.getInitParameter("encoding");
        if (encoding == null) {
            encoding = "UTF-8";
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 设置请求的字符编码
        request.setCharacterEncoding(encoding);
        // 设置响应的字符编码
        response.setCharacterEncoding(encoding);
        // 将请求传递给下一个过滤器或目标 Servlet
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 释放过滤器占用的资源
    }
}
java 复制代码
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(filterName = "CharacterEncodingFilter", urlPatterns = "/*", initParams = {
        @WebInitParam(name = "encoding", value = "UTF-8")
})
public class CharacterEncodingFilter implements Filter {
    private String encoding;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        encoding = filterConfig.getInitParameter("encoding");
        if (encoding == null) {
            encoding = "UTF-8";
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        request.setCharacterEncoding(encoding);
        response.setCharacterEncoding(encoding);
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

过滤器的生命周期

  • 初始化 :当 Web 应用启动时,Servlet 容器会创建过滤器实例,并调用 init() 方法进行初始化。init()方法只会被调用一次。
  • 过滤处理 :每次请求匹配到过滤器的 url-pattern 时,Servlet 容器会调用 doFilter() 方法对请求进行过滤处理。
  • 销毁 :当 Web 应用停止时,Servlet 容器会调用 destroy() 方法销毁过滤器实例,释放过滤器占用的资源。

什么是 Spring MVC?

Spring MVC 是对 Servlet 的封装和扩展:

1、在传统 Servlet 开发中,每个请求可能需要对应一个具体的 Servlet 类来处理。而在 Spring MVC 里,DispatcherServlet 作为前端控制器,接收所有客户端请求,统一调度和分发。感觉有点面向对象的味道在里面,客户端相当于接口,各种客户端的请求相当于各种实现类的实例,对于后端来说,只有一个功能,那就是接收客户端的请求,所以只需要一个 Servlet ,那就是 DispatcherServlet。

2、MVC 架构和注解驱动:基于 '@Controller' 、'@RequestMapping' 、'@GetMapping'等注解实现请求映射,以及业务逻辑和视图渲染的解藕。

工作流程

1、DispatcherServlet接受Http请求,根据URL去HandlerMapping中寻找对于的handler(controller)。

2、HandlerAdapter去调用对应的handler(controller)。

3、调用controller返回ModelAndView给中央处理器。

4、ViewResolver解析ModelAndView中的逻辑view找到真实的view。

5、中央处理器将model传给view。

6、将view作为结果返回给浏览器。

什么是拦截器?

Spring MVC 中的重要组件,对处理器进行预处理和后处理。实现HandlerInterceptor 接口,实现三个方法:preHandlepostHandleafterCompletion,例如:

java 复制代码
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyInterceptor implements HandlerInterceptor {
    // 在处理器处理请求之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Pre-handle method is called");
        // 返回 true 表示继续执行后续的处理器和拦截器
        return true; 
    }

    // 在处理器处理请求之后,视图渲染之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("Post-handle method is called");
    }

    // 在整个请求处理完成之后执行,包括视图渲染之后
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("After-completion method is called");
    }
}
typescript 复制代码
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器
        registry.addInterceptor(new MyInterceptor())
                // 指定拦截的路径
               .addPathPatterns("/**") 
                // 排除不拦截的路径
               .excludePathPatterns("/login"); 
    }
}

过滤器和拦截器的区别?

  1. 层次不同:过滤器作用在 Servlet 层面,拦截器作用在控制器层面。
  2. 功能侧重不同:过滤器侧重于全局的预处理和后处理,例如字符编码设置。拦截器侧重于对请求处理过程进行增强和控制,例如日志记录、数据预处理。
相关推荐
num_killer7 小时前
小白的Langchain学习
java·python·学习·langchain
期待のcode7 小时前
Java虚拟机的运行模式
java·开发语言·jvm
程序员老徐7 小时前
Tomcat源码分析三(Tomcat请求源码分析)
java·tomcat
a程序小傲7 小时前
京东Java面试被问:动态规划的状态压缩和优化技巧
java·开发语言·mysql·算法·adb·postgresql·深度优先
仙俊红7 小时前
spring的IoC(控制反转)面试题
java·后端·spring
阿湯哥8 小时前
AgentScope Java 集成 Spring AI Alibaba Workflow 完整指南
java·人工智能·spring
小楼v8 小时前
说说常见的限流算法及如何使用Redisson实现多机限流
java·后端·redisson·限流算法
与遨游于天地8 小时前
NIO的三个组件解决三个问题
java·后端·nio
czlczl200209258 小时前
Guava Cache 原理与实战
java·后端·spring
yangminlei8 小时前
Spring 事务探秘:核心机制与应用场景解析
java·spring boot