过滤器和拦截器的区别

过滤器和拦截器的区别,第1张

一、过滤器

Filter 是对客户端访问资源的过滤,符合条件放行,不符合条件不放行,并且可以对目标资源访问前后进行逻辑处理。

1、过滤器生命周期

Filter对象的生命周期:

  • 实例化:服务器启动时,实例化;
  • 初始化:调用 init() 方法(只会被初始化一次);
  • 过滤:当用户请求与过滤器关联的 url 匹配时,调用 doFilter() 方法。FilterChain 参数可以调用chain.doFilter 方法,将请求传给下一个过滤器(或目标资源),或利用转发、重定向将请求转发到其他资源;
  • 销毁:服务器关闭时销毁 Filter 对象,可以通过 destroy() 方法在 Filter 对象销毁前进行处理。

过滤器(Filter)属于 Servlet 的范畴,通过实现 javax.servlet.Filter 接口来实现功能,该接口定义了3个方法:

  • void init(FilterConfig filterConfig):容器启动,初始化 Filter 时会被调用,整个生命周期只会被调用一次,可用于完成 Filter 的初始化。其中参数 filterConfig (每次创建 Filter 的时候,也会同时创建一个 FilterConfig)代表该 Filter 对象配置信息的对象;
  • void doFilter(ServletRequest request, ServletResponse response, FilterChain chain): 实现过滤功能,就是通过该方法对每个请求增加额外的处理。其参数 FilterChain(过滤器链对象)的 doFilter() 方法可以放行请求;
  • void destroy():用于 Filter 销毁前,完成某些资源的回收。
2、过滤器执行顺序

如果配置了多个相同路径的过滤器,服务器会按照过滤器定义的先后顺序组装成一条链。 过滤器链的执行顺序:依次执行每个过滤器放行方法前的代码,最后交给 Servlet 的 Service() 方法,处理后倒序依次执行每个过滤器放行方法后的代码,然后返回给用户请求。

3、过滤器的配置使用

过滤器的配置使用有两种方式:

  • 通过 @ServletComponentScan 和 @WebFilter 注解配置;
  • 自定义注解,通过配置类进行配置。
3.1、通过注解配置过滤器

使用注解配置过滤器步骤如下:

  • 启动类上添加 @ServletComponentScan;
  • 自定义过滤器,实现 javax.servlet.Filter 接口,在自定义过滤器上添加注解 @WebFilter。
/**
 * @Des 自定义过滤器
 * @Author jidi 
 * @Email jidi_jidi@163.com
 */
@Slf4j
@WebFilter(filterName = "loginFilter", urlPatterns = "/*")
public class LoginFilter implements Filter {
    /**
     * Spring 框架提供的用于路径比较的类:路径匹配器,支持通配符
     */
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    /**
     * 定义不需要处理的请求路径
     */
    private static final String[] URLS = new String[]{
            "/employee/login",
            "/employee/logout",
            "/backend/**",
            "/front/**",
            "/common/**"
    };

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("LoginFilter 初始化.......");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 获取本次请求的 URI
        String requestURI = request.getRequestURI();
        log.info("拦截到请求:{}", requestURI);

        // 判断本次请求, 是否需要登录, 才可以访问
        boolean check = checkURI(URLS, requestURI);

        // 如果不需要拦截,则直接放行
        if (check){
            log.info("本次{}请求不需要处理!", requestURI);
            filterChain.doFilter(request, response);
            return;
        }

        // 判断登录状态,如果已登录,则直接放行
        Long userId = (Long) request.getSession().getAttribute("user");
        if (userId != null){
            log.info("用户已登录,用户ID为:{}", userId);
            filterChain.doFilter(request, response);
            return;
        }

        // 如果未登录, 则返回未登录结果
        // 通过输出流方式,向客户端页面响应数据
        log.info("用户未登录!");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(JSON.toJSONString(Result.noPermission()));
    }

    @Override
    public void destroy() {
        log.info("LoginFilter 销毁.......");
    }

    /*
     * 路径匹配,检查本次请求是否需要放行
     */
    public boolean checkURI(String[] urls, String requestURI){
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI);
            if (match){
                return true;
            }
        }
        return false;
    }
}

注意:如果有多个通过注解方式配置的过滤器,是无法指定过滤器执行顺序的(默认的执行顺序由 @WebFilter 注解的 filterName 属性根据过滤器名称排序决定)。

3.2、通过配置类配置过滤器

使用配置类配置过滤器步骤如下:

  • 自定义过滤器,实现 javax.servlet.Filter 接口;
  • 自定义过滤器配置类,指定过滤器拦截配置。
/**
 * @Des 过滤器配置
 * @Author jidi
 * @Email jidi_jidi@163.com
 */
@Configuration
public class FilterConfig {

    @Autowired
    private LoginFilter loginFilter;

    @Autowired
    private DepartmentFilter departmentFilter;

    @Bean
    public FilterRegistrationBean loginFilterRegistration() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(loginFilter);
        filterRegistrationBean.addUrlPatterns("/*");
        //order的数值越小 则优先级越高
        filterRegistrationBean.setOrder(10);
        filterRegistrationBean.setName("loginFilter");
        return filterRegistrationBean;
    }


    @Bean
    public FilterRegistrationBean departmentFilterRegistration() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(departmentFilter);
        filterRegistrationBean.addUrlPatterns("/*");
        //order的数值越小 则优先级越高
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.setName("departmentFilter");
        return filterRegistrationBean;
    }
}

通过配置类定制过滤器配置信息,可以指定过滤器的执行顺序。

二、拦截器

SpringMVC拦截器(Interceptor)实现对每一个请求处理前后进行相关的业务处理。在SpringMVC中定义一个拦截器非常简单,主要有4种方式:

  • 实现 HandlerInterceptor 接口;
  • 继承实现了 HandlerInterceptor 接口的类,比如 Spring 已经提供的实现了 HandlerInterceptor 接口的抽象类 WebRequestHandlerInterceptorAdapter;
  • 实现 WebRequestInterceptor 接口;
  • 继承实现了 WebRequestInterceptor 接口的类。

实现了拦截器之后,可以通过重写 WebMvcConfigurer 接口的 addInterceptors 方法注册拦截器。

1、HandlerInterceptor 接口

HandlerInterceptor 接口定义了如下三个方法:

  • preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法:在请求处理之前被调用。Interceptor 是链式调用的,可以存在多个 Interceptor。Interceptor 的调用会依据声明顺序依次执行,最先执行的都是 preHandle 方法,可在该方法中进行一些前置(预)处理,也可进行判断来决定是否要继续执行。当返回为 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当返回值为 true 时,会继续调用下一个 Interceptor 的 preHandle 方法,执行完最后一个 Interceptor 后会调用当前请求的 Controller 方法;
  • postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法:会在 Controller 方法调用之后,DispatcherServlet 进行渲染视图之前被调用,所以可以对 Controller 处理之后的 ModelAndView 对象进行 *** 作。postHandle 方法被调用的方向跟 preHandle 是相反的,先声明的 Interceptor 的 postHandle 方法反而会后执行;
  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法:该方法也是需要当前对应的 Interceptor 的 preHandle 方法的返回值为true时才会执行,会在整个请求结束之后被调用,也就是在 DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要是用于进行资源清理。
2、WebRequestInterceptor 接口

WebRequestInterceptor 中也定义了三个方法,这三个方法都传递了同一个参数 WebRequest ,WebRequest 是Spring 定义的一个接口,它里面的方法定义都基本跟 HttpServletRequest 一样,在WebRequestInterceptor 中对 WebRequest 进行的所有 *** 作都将同步到 HttpServletRequest 中,然后在当前请求中一直传递:

  • preHandle(WebRequest request) 方法:该方法将在请求处理之前进行调用,也就是说会在Controller 方法调用之前被调用。这个方法跟 HandlerInterceptor 中的 preHandle 是不同的,主要区别在于该方法的返回值是 void ,所以一般用它来进行资源的准备工作;
  • postHandle(WebRequest request, ModelMap model) 方法:该方法将在请求处理之后,也就是在 Controller 方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型 ModelMap 来改变数据的展示。该方法有两个参数:
    • WebRequest 对象是用于传递整个请求数据的,比如在 preHandle 中准备的数据都可以通过WebRequest 来传递和访问;
    • ModelMap 就是Controller 处理之后返回的 Model 对象,可以通过改变它的属性来改变返回的Model 模型;
  • afterCompletion(WebRequest request, Exception ex) 方法:该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。所以在该方法中可以进行资源的释放 *** 作:
    • Exception 参数表示的是当前请求的异常对象,如果在Controller 中抛出的异常已经被Spring 的异常处理器给处理了的话,那么这个异常对象就是是null 。
3、拦截器执行顺序

如果配置了拦截器,首先会执行 preHandle() 方法,如果方法返回值为true,则会继续向下执行处理器中的方法,否则不再向下执行;在业务控制器 Controller 处理完请求后,会执行 postHandle() 方法,然后会通过 DispatcherServlet 向客户端返回响应;在 DispatcherServlet 处理完请求后,才会执行 afterCompletion() 方法:

如果配置了多个拦截器,每个拦截器都会按照配置的顺序执行,但是 preHadle() 方法会按照配置文件拦截器的配置顺序执行,postHandle() 方法和 afterCompletion() 方法则会按照配置顺序的相反顺序执行:

4、自定义拦截器

自定义拦截器步骤如下:

  • 自定义拦截器类实现拦截器接口;
  • 配置拦截器。
/**
 * @Des 定义拦截器
 * @Author jidi
 * @Email jidi_jidi@163.com
 */
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取本次请求的URI
        String requestURI = request.getRequestURI();
        log.info("LoginInterceptor 拦截到请求:{}", requestURI);

        HttpSession session = request.getSession();
        Object username = session.getAttribute("username");
        if(null == username){
            log.info("用户未登录!");
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().write(JSON.toJSONString(Result.noPermission()));
            return false;
        }
        log.info("用户已登录!");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        log.info("postHandle 方法被执行....");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        log.info("afterCompletion 方法被执行....");
    }
}


/**
 * @Des 配置拦截器
 * @Author jidi
 * @Email jidi_jidi@163.com
 */
@Configuration
public class WebappAdapter implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 不拦截的请求
        List<String> patterns = new ArrayList<>();
        patterns.add("/bootstrap3/**");

        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(patterns)
                .order(1);
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}
三、拦截器和过滤器的区别
  • 使用范围与规范不同:Filter 是 Servlet 规范中定义的,只能用于 Web 程序中,依赖于 Servlet 容器;拦截器是 Spring 的组件,不依赖 Servlet 容器;
  • 使用资源不同:拦截器可以使用Spring里的任何资源、对象,例如 Service 对象、数据源、事务管理等,通过IOC注入到拦截器即可;而 Filter 则不能;
  • 作用范围不同:Filter 对几乎所有的请求起作用;而 Interceptor 只能对 action 请求起作用。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/731716.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-27
下一篇 2022-04-27

发表评论

登录后才能评论

评论列表(0条)

保存