一文搞懂过滤器和拦截器

什么是 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. 功能侧重不同:过滤器侧重于全局的预处理和后处理,例如字符编码设置。拦截器侧重于对请求处理过程进行增强和控制,例如日志记录、数据预处理。
相关推荐
快来卷java9 分钟前
MySQL篇(一):慢查询定位及索引、B树相关知识详解
java·数据结构·b树·mysql·adb
凸头44 分钟前
I/O多路复用 + Reactor和Proactor + 一致性哈希
java·哈希算法
慵懒学者1 小时前
15 网络编程:三要素(IP地址、端口、协议)、UDP通信实现和TCP通信实现 (黑马Java视频笔记)
java·网络·笔记·tcp/ip·udp
anda01091 小时前
11-leveldb compact原理和性能优化
java·开发语言·性能优化
Pasregret2 小时前
04-深入解析 Spring 事务管理原理及源码
java·数据库·后端·spring·oracle
Micro麦可乐2 小时前
最新Spring Security实战教程(七)方法级安全控制@PreAuthorize注解的灵活运用
java·spring boot·后端·spring·intellij-idea·spring security
csjane10792 小时前
Redis原理:rename命令
java·redis
牛马baby2 小时前
Java高频面试之并发编程-02
java·开发语言·面试
uhakadotcom2 小时前
EventBus:简化组件间通信的利器
android·java·github
纪元A梦2 小时前
分布式锁算法——基于ZooKeeper的分布式锁全面解析
java·分布式·算法·zookeeper