Filter 和 Interceptor区别

Filter和Interceptor的联系和区别 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/456610808?utm_source=wechat_session&utm_medium=social&utm_oi=1197944133762072576&utm_campaign=shareopn

Filter简述和@Filter注解使用》主要对过滤器Filter进行了简单的说明,本文将对拦截器Interceptor进行简单讲解,并通过几个例子对它们的差异进行简要分析。

目录

拦截器Interceptor简介

自定义拦截器Interceptor

注册自定义拦截器Interceptor

过滤器Filter和拦截器Interceptor区别

过滤器和拦截器如何注入依赖服务

过滤器和拦截器如何指定加载顺序

过滤器Filter和拦截器Interceptor使用场景

拓展Servlet和Controller的区别是什么?


拦截器Interceptor简介

一个应用中可以定义多个拦截器,spring在项目启动时,会将这些拦截器注册进来,并按照默认规则进行排序。如果是自定义的拦截器,可手动设置拦截器调用的先后顺序。

各拦截器是链式调用,一个请求可以触发多个拦截器,每个拦截器的调用会按照它加载到spring中的顺序依次执行。

拦截器中有3个方法,功能如下:

(1)preHandle:该方法在调用Controller方法或获取静态资源前被调用(静态资源包括html、js等)。

(2)postHandle:该方法在调用Controller方法或获取静态资源后,但是视图还没有被渲染前调用。

(3)afterCompletion:该方法在视图渲染后进行调用,主要用来清除资源。

自定义拦截器Interceptor

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("调用preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("调用postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("调用afterCompletion");
    }
}

注册自定义拦截器Interceptor

编写配置类并实现WebMvcConfigurer接口。

@Configuration
public class MyWebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
    }
}

过滤器Filter和拦截器Interceptor区别

过滤器和拦截器都可以实现例如编码设置、日志记录、权限控制等功能,但是二者还是有很多区别的。

(1)实现原理不同

  • Filter是基于函数回调实现的:

每个自定义过滤器都会实现一个doFilter()方法,这个方法有一个关键参数FilterChain。它是一个回调接口,ApplicationFilterChain是它的具体实现类,该类内部也有一个doFilter()方法,这个方法就是回调方法(ps:可以理解为方法递归调用,查看源码比较容易理解)。

假设有2个滤器,调用流程图如下(ps:画的比较抽象):

两个过滤器执行流程

  • Interceptor是基于反射实现的:

为什么说拦截器是基于反射实现的呢?个人理解在拦截器内部的三个方法中,都有一个共同的参数handler,这个参数里包含的信息比较丰富。包含该请求所对应的方法、方法所在的Controller、方法参数等信息,而这些信息都是spring通过反射加载进来的。正是由于这些丰富的参数,使得拦截的功能相比过滤器功能更强大。

handler参数信息

(2)使用范围不同

过滤器Filter实现了javax.servlet.Filter接口,也就是说过滤器的使用要依赖于Tomcat等容器,所以它只能在web程序中使用。

拦截器Interceptor实现了org.springframework.web.servlet接口,它是由Spring容器进行管理,并不依赖Tomcat等容器,既可以应用在web程序中,也可以应用在非web程序中。

(3)触发时机不同

过滤器Filter是在请求进入Tomcat等容器后,servlet处理之前进行调用的。

拦截器Interceptor是在请求进入servlet后,执行Controller之前进行调用的。

(4)拦截范围不同

过滤器Filter几乎可以拦截所有进入容器的请求。

拦截器Interceptor只会对Controller请求或访问static目录下的静态资源请求起作用。

(5)初始化时机不同

过滤器Filter是随着Tomcat等web容器启动时而进行初始化。

拦截器Interceptor时随着spring启动而进行初始化。

过滤器和拦截器如何注入依赖服务

在实际开发中,当使用到过滤器或拦截器时,难免会引入一些依赖的service服务。下面就通过例子进行简要说明:

Filter依赖service:直接采用注解@Autowired即可。

@WebFilter(urlPatterns = {"/user/*"})
@Log4j2
public class MyFilter implements Filter {
    @Autowired
    private UserService userService;
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        log.info("Filter获取到请求地址:" + httpServletRequest.getServletPath());
        filterChain.doFilter(request, response);

        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        log.info("Filter获取到响应类型:" + httpServletResponse.getContentType());
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Filter随着项目的启动而启动,只初始化一次");
    }

    @Override
    public void destroy() {
        System.out.println("Filter随着web项目的停止而销毁,完成资源回收");
    }
}

Interceptor依赖service:直接采用注解@Autowired,但是在将拦截器注入到spring容器中时,不能自己通过new来进行创建。需要将拦截器当做一个普通的bean注入到spring容器中,这样就可以将service注入到拦截器中。

public class MyInterceptor implements HandlerInterceptor {
    @Autowired
    private UserService userService;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("调用preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("调用postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("调用afterCompletion");
    }
}

@Configuration
public class MyWebConfig implements WebMvcConfigurer {

    @Bean
    public MyInterceptor getMyInterceptor(){
        return new MyInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // getMyInterceptor()这种注册方式可以在拦截器里注入bean
        registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**");
        // 这种注册方式由于是自己new出来的,所以在拦截器里注册的bean都为null
        registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
    }
}

过滤器和拦截器如何指定加载顺序

(1)Filter:需要通过配置类指定加载顺序,值越小,越先执行。采用@WebFilter无法指定顺序。

@Bean
public FilterRegistrationBean myFilter(){
    MyFilter myFilter = new MyFilter();
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
    filterRegistrationBean.setUrlPatterns(Arrays.asList("/user/*"));
    filterRegistrationBean.setOrder(2);
    return filterRegistrationBean;
}

(2)Interceptor:需要通过配置类指定加载顺序,值越小,越先执行。

@Configuration
public class MyWebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // getMyInterceptor()这种注册方式可以在拦截器里注入bean
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").order(1);
        // 这种注册方式由于是自己new出来的,所以在拦截器里注册的bean都为null
        registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**").order(2);
    }
}

过滤器Filter和拦截器Interceptor使用场景

二者相比拦截器功能更强大些,Filter能做的事情,它都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要用来设置字符编码、过滤敏感词汇和URL级别的简单权限控制。如果需要记录比较详细的信息或比较复杂的权限管理,还是建议用拦截器实现。

拓展Servlet和Controller的区别是什么?

使用Servlet可以收集来自网页表单的用户输入,还可以动态创建网页。DispatcherServlet是SpringMVC中唯一的Servlet,Servlet容器(Tomcat)把所有的请求都转发到DispatcherServlet,然后通过HandlerMapping把请求路由到具体的Controller中。因此,Controller只是一个普通的Java Bean

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洋气月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值