写点什么

谈谈 Spring 的过滤器和拦截器

  • 2024-05-14
    福建
  • 本文字数:4369 字

    阅读完需:约 14 分钟

前言


我们在进行 Web 应用开发时,时常需要对请求进行拦截或处理,故 Spring 为我们提供了过滤器和拦截器来应对这种情况。那么两者之间有什么不同呢?本文将详细讲解两者的区别和对应的使用场景。

(本文的代码实现首先是基于 SpringBoot,Spring 的实现方式仅简单描述)




1. 过滤器


1.1. 什么是过滤器


过滤器(Filter),是 Servlet 规范规定的,在 Servlet 前执行的。用于拦截和处理 HTTP 请求和响应,可用于身份认证、授权、日志记录和设置字符集(CharacterEncodingFilter)等场景。


过滤器位于整个请求处理流程的最前端,因此在请求到达 Controller 层前,都会先被过滤器处理。


过滤器可以拦截多个请求或响应,一个请求或响应也可以被多个过滤器拦截


1.2. 如何创建过滤器


Filter 的生命周期对应的三个关键方法:



下面将介绍二种方法创建 Filter。


1.2.1 实现 Filter 接口


1.创建 Filter 处理类,实现javax.servlet.Filter接口,加上@WebFilter注解配置拦截 Url,但是不能指定过滤器执行顺序,也可通过web.xml配置。

@WebFilter(urlPatterns = "/*")public class MyFilter implements Filter {     @Override    public void init(FilterConfig filterConfig) throws ServletException {        // 用于完成 Filter 的初始化        Filter.super.init(filterConfig);    }     @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {                System.out.println("过滤器已经拦截成功!!!");         // 执行该方法之前,即对用户请求进行预处理;执行该方法之后,即对服务器响应进行后处理。        chain.doFilter(request,response);    }     @Override    public void destroy() {        // 用于 Filter 销毁前,完成某些资源的回收;        Filter.super.destroy();    }}
复制代码


2.在启动类添加注解@ServletComponentScan ,让 Spring 可以扫描到。

@SpringBootApplication@ServletComponentScanpublic class MyFilterDemoApplication {     public static void main(String[] args) {        SpringApplication.run(MyFilterDemoApplication.class, args);    } }
复制代码


3.创建 Controller 发起 Url 请求。

@RestControllerpublic class MyFilterController {     @GetMapping("/testFilter")    public String testFilter(){        return "Hello World";    }}
复制代码


拦截结果



1.2.2. 通过 @Component 注解


1.创建 Filter 处理类,实现javax.servlet.Filter接口,加@Component注解。


  • 可以使用@Order注解保证过滤器执行顺序,不加则按照类名排序。


  • 过滤器不能指定拦截的 url , 只能默认拦截全部

@Component@Order(1)public class MyComponentFilter1 implements Filter {    @Override    public void init(FilterConfig filterConfig) throws ServletException {        Filter.super.init(filterConfig);    }     @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {                System.out.println("我是过滤器1已经拦截成功!!!");        chain.doFilter(request,response);    }     @Override    public void destroy() {        Filter.super.destroy();    }}
复制代码


@Component@Order(2)public class MyComponentFilter2 implements Filter {    @Override    public void init(FilterConfig filterConfig) throws ServletException {        Filter.super.init(filterConfig);    }     @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException             System.out.println("我是过滤器2已经拦截成功!!!");        chain.doFilter(request,response);    }     @Override    public void destroy() {        Filter.super.destroy();    }}
复制代码


2-3 步骤同 1.2.1,结果如下。




2. 拦截器


2.1. 什么是拦截器


拦截器(Interceptor),和 Servlet 无关,由 Spring 框架实现。可用于身份认证、授权、日志记录、预先设置数据以及统计方法的执行效率等。一般基于 Java 的反射机制实现,属于 AOP 的一种运用。


目前了解的 Spring 中的拦截器有:


  • HandlerInterceptor

  • MethodInterceptor


2.2. HandlerInterceptor 拦截器


2.2.1 简介


HandlerInterceptor 类似 Filter,拦截的是请求地址 ,但提供更精细的的控制能力,这里注意下必须过 DispatcherServlet 的请求才会被拦截。


它允许你在请求处理前、处理后以及视图渲染完成前执行自定义逻辑,可以用来对请求地址做一些认证授权、预处理,也可以计算一个请求的响应时间等,还可以处理跨域(CORS)问题


简单的执行流程描述:


  1. 请求到达 DispatcherServlet,然后发送至 Interceptor,执行 preHandler;

  2. 请求到达 Controller,请求结束后,执行 postHandler。


2.2.2 如何实现


  1. 创建 Interceptor 类,实现HandlerInterceptor接口,重写 3 个方法,加@Component注解。



@Componentpublic class MyHandlerInterceptor implements HandlerInterceptor {    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {                //请求开始时间        long startTime = System.currentTimeMillis();        request.setAttribute("startTime", startTime);        System.out.println("startTime : " +  new Date(startTime));        return true;    }     @Override    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {                long startTime = (Long)request.getAttribute("startTime");        long endTime = System.currentTimeMillis();        // 统计耗时        long executeTime = endTime - startTime;        System.out.println("executeTime : " + executeTime + "ms");     }     @Override    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {                HandlerInterceptor.super.afterCompletion(request, response, handler, ex);    }}
复制代码


2.配置拦截器,实现WebMvcConfigurer接口,加@Configuration注解并重写addInterceptors方法。

@Configurationpublic class MyWebConfigurer implements WebMvcConfigurer {     @Resource    private MyHandlerInterceptor myHandlerInterceptor;    @Override    public void addInterceptors(InterceptorRegistry registry) {        List<String> patterns = new ArrayList<>();         patterns.add("/test/handlerInterceptor");         registry.addInterceptor(myHandlerInterceptor)                .addPathPatterns(patterns) // 需要拦截的请求                .excludePathPatterns(); // 不需要拦截的请求    }}
复制代码


拦截结果如下:



Spring 项目如何实现?


可通过使用mvc:interceptors标签来声明需要加入到 SpringMVC 拦截器链中的拦截器。


2.3. MethodInterceptor 拦截器


2.3.1. 简介


MethodInterceptor 是 AOP 中的拦截器,它拦截的目标是方法,可以不是 Controller 中的方法。

在对一些普通的方法上的拦截可以使用该拦截器,这是 HandlerInterceptor 无法实现的。


可用来进行方法级别的身份认证、授权以及日志记录等,也可基于自定义注解实现一些通用的方法增强功能


2.3.2. 如何实现


MethodInterceptor 是基于 AOP 实现的,所以根据不同的代理有多种实现方式,更多的实现方式和原理我将在整理 Spring AOP 的时候详细接受。


这里我将介绍通过BeanNameAutoProxyCreator自动代理实现拦截。该类是基于 Bean 名称的自动代理,可以针对特定的 Bean 进行个性化的 AOP 配置。


1.创建简单的需要拦截的方法。

public interface UserService {    public String getUser();}
复制代码


@Componentpublic class UserServiceImpl implements UserService{     @Override    public String getUser() {        return "我是福星";    }}
复制代码


2.创建 Interceptor 类,实现MethodInterceptor接口,重写invoke方法,加@Component注解。

@Componentpublic class MyMethodInterceptor implements MethodInterceptor {     @Override    public Object invoke(MethodInvocation invocation) throws Throwable {        System.out.println("进入拦截,方法执行前,拦截方法是:" + invocation.getMethod().getName());        Object result = invocation.proceed();        System.out.println("方法执行后");        return result;    } }
复制代码


3.配置自动代理,加@Configuration注解并创建自动代理BeanNameAutoProxyCreator

@Configurationpublic class MyMethodConfigurer {    @Resource    private MyMethodInterceptor myMethodInterceptor;      @Bean    public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {        // 使用BeanNameAutoProxyCreator来创建代理        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();         // 指定一组需要自动代理的Bean名称,Bean名称可以使用*通配符        beanNameAutoProxyCreator.setBeanNames("user*");         //设置拦截器名称,这些拦截器是有先后顺序的        beanNameAutoProxyCreator.setInterceptorNames("myMethodInterceptor");        return beanNameAutoProxyCreator;    } }
复制代码


发起请求后,调用该方法前会进行拦截。




3. 总结


过滤器一般用于对 Servlet 请求和响应进行通用性的处理,通常关注请求和响应内容,而不涉及具体的业务逻辑。而拦截器用于对 SpringMVC 的请求和响应进行特定的业务处理,通常与控制器层的请求处理有关。


不论是过滤器和拦截器,都可以有多个。执行顺序上拦截器是由配置中的顺序决定,而过滤器可通过@Component+@Order决定,也可由web.xml文件中的配置顺序决定。


总的来说,拦截器的使用更加灵活,Filter 能做的事情,拦截器也能做。Filter 一般用于对 URL 请求做编码处理、过滤无用参数、安全校验(比如登陆态校验),如果涉及业务逻辑上的,还是建议用拦截器。


文章转载自:fuxing.

原文链接:https://www.cnblogs.com/fuxing/p/18188764

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
谈谈 Spring 的过滤器和拦截器_Java_快乐非自愿限量之名_InfoQ写作社区