Filter和Interceptor的区别和应用领域


前言

在项目开发中,我们经常会遇到需要拦截器(Interceptor)或者过滤器(Filter)的需求,但是我们什么时候该用过滤器,什么时候该用拦截器呢?


一、区别

在这里插入图片描述
我们来一一介绍一下

1、Filter

Filter是由Servlet提供的组件,实现接口javax.servlet.Filter,其内部基于回调函数实现,通过调用FilterChain类下的doFilter方法进行不断回调。
在客户端请求进入Servlet之前进行调用处理。
其会对所有请求进行增强处理。

@WebFilter("/*")
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String url = request.getRequestURI();
        if (url.contains("/admin.jsp")||url.contains("/register.jsp")){
           filterChain.doFilter(request,response);
           return;
        }
        if (url.contains("/js")||url.contains("/css")||url.contains("/fonts")||url.contains("/images")||url.contains("/lib")||url.contains("/layui")){
            filterChain.doFilter(request,response);
            return;
        }
        String user = (String) request.getSession().getAttribute("User");
        if(user!=null){
            filterChain.doFilter(request,response);
            return;
        }
        if (url.contains("/home")||url.contains("/register")) {
            filterChain.doFilter(request,response);
            return;
}
        response.sendRedirect("admin.jsp");
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

我们来分析一下原理:

  1. 当服务器初始化时,Filter中的init()方法会被执行调用,创建Filter。
  2. 当客户端请求进tomcat服务器之后,Filter处理对应的请求进入doFilter方法
  3. 当服务器关闭时,会调用Filter的destroy方法来进行销毁操作。

2.Interceptor

拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。其是基于反射实现的。Interceptor仅针对SpringMVC的访问进行拦截增强。
看一下我这边在springboot中如何使用拦截器的:

@Component
@Slf4j
@RequiredArgsConstructor
public class TokenInterceptor implements HandlerInterceptor {

    private final IUserService userService;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("X-Token");
        if (token!=null){
            Long userId = JwtHelper.getUserId(token);
            String username = JwtHelper.getUsername(token);
            User user = userService.getOne(new LambdaQueryWrapper<User>()
                    .eq(User::getId, userId)
                    .eq(User::getUsername, username)
            );
            if (ObjectUtil.isNotNull(user)) {
                log.debug("验证通过");
                return true;
            }
        }
        log.debug("验证失败");
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(JSON.toJSONString(Result.fail(20003,"无效,请重新登录")));
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

首先定然是要先实现HandlerInterceptor这个接口,别忘了把他交给spring管理。
同时还有配置类:

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

    private final TokenInterceptor tokenInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(
                        "/user/login",
                        "/user/info",
                        "/user/logout",
                        "/doc.html/**",
                        "/webjars/**",
                        "/swagger-resources/**"
                );
    }
}

我们来看看拦截器里面的方法:

前置处理方法

控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法。

  • 返回false表示拦截,即不调用控制器方法。
  • 返回true表示不拦截,放行。
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("preHandle()方法被执行!");
    return true;
}

request:请求对象
response:响应对象
handler:被调用的处理器对象,本质上是一个方法对象,对反射中的Method对象进行了再包装

使用handler参数,可以获取方法的相关信息

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HandlerMethod hm = (HandlerMethod)handler;
    String methodName = hm.getMethod().getName();//可以获取方法的名称
    System.out.println("preHandle..."+methodName);
    return true;
}

后置处理方法

原始方法运行后运行,如果原始方法被拦截,则不执行

@Override
public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object handler, ModelAndView mv) throws Exception {
    System.out.println("postHandle()方法被执行!");
}

modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整

因为咱们现在都是返回json数据,所以该参数的使用率不高。

完成处理方法

拦截器最后执行的方法,无论原始方法是否执行

@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object handler, Exception ex) throws Exception {
    System.out.println("afterCompletion()方法被执行!");
}

:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理 ,因为我们现在已经有全局异常处理器类,所以该参数的使用率也不高。

这三个方法中,最常用的是preHandle(),在这个方法中可以通过返回值来决定是否要进行放行,我们可以把业务逻辑放在该方法中,如果满足业务则返回true放行,不满足则返回false拦截。

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

(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

应用领域比较少。在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符、敏感词过滤、响应信息压缩、控制权限、控制转向、做一些业务逻辑判断等。

Interceptor

处理各种复杂请求,比Filter功能更加强大

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Gunalaer

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

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

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

打赏作者

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

抵扣说明:

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

余额充值