• SpringBoot:过滤器与拦截器


    过滤器与拦截器

    滤器和拦截器都属于面向切面编程的具体实现。而两者的主要区别包括以下几个方面:

    1. Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。

    2. Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理的方式来执行。

    3. Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。

    过滤器的配置

    实例:通过过滤器来实现记录请求执行时间的功能

    import javax.servlet.*;
    import java.io.IOException;
    
    public class MyFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            long startTime = System.currentTimeMillis();
            System.out.println("请求被执行了·····");
            filterChain.doFilter(servletRequest, servletResponse);
            long endTime = System.currentTimeMillis();
            System.out.println("请求执行了"+(endTime-startTime)%1000+"s");
        }
    }
    
    

    这段代码的逻辑比较简单,就是在方法执行前先记录时间戳,然后通过过滤器链完成请求的执行,在返回结果之间计算执行的时间。这里需要注意的是这个类必须继承Filter类,这个是Servlet的规范,这个跟以前的Web项目没区别。

    FilterRegistrationBean

    但是,有了过滤器类以后,以前的web项目可以在web.xml中进行配置,但是spring boot项目并没有web.xml这个文件,那怎么配置?在Spring boot中,我们需要FilterRegistrationBean来完成配置:

    @Configuration
    public class FilterConfig {
    
        @Bean
        public FilterRegistrationBean filterRegistrationBean(){
            FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
            filterRegistrationBean.setFilter(new MyFilter());
            filterRegistrationBean.addUrlPatterns("/*");
            filterRegistrationBean.setName("myFilter");
            filterRegistrationBean.setOrder(1);
            return filterRegistrationBean;
        }
    }
    

    这样配置就完成了,需要配置的选项主要包括:

    • 实例化Filter类;
    • 然后指定url的匹配模式;
    • 设置过滤器名称和执行顺序。

    这个过程和在web.xml中配置其实没什么区别,只是形式不同而已。现在我们可以启动服务器访问任意URL:

    @WebFilter

    除了通过 FilterRegistrationBean 来配置以外,还有一种更直接的办法,直接通过注解就可以完成了:

    @WebFilter(urlPatterns = "/*",filterName = "myFilter2")
    public class MyFilter2 implements Filter {
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            long startTime = System.currentTimeMillis();
            System.out.println("filter2:请求被执行了·····");
            filterChain.doFilter(servletRequest, servletResponse);
            long endTime = System.currentTimeMillis();
            System.out.println("filter2:请求执行了"+(endTime-startTime)%1000+"s");
        }
    }
    

    这里直接用@WebFilter就可以进行配置,同样,可以设置url匹配模式,过滤器名称等。这里需要注意一点的是@WebFilter这个注解是Servlet3.0的规范,并不是Spring boot提供的。除了这个注解以外,我们还需在配置类中加另外一个注解:@ServletComponetScan,指定扫描的包:

    @SpringBootApplication
    @ServletComponentScan(basePackages = "com.example")     //扫描servlet组件
    public class FiltersandinterceptorsdemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(FiltersandinterceptorsdemoApplication.class, args);
        }
    
    }
    

    现在,我们再来访问一下任意URL:

    可以看到,我们配置的两个过滤器都生效了。细心的读者会发现,第二个Filter我们并没有指定执行的顺序,但是却在第一个Filter之前执行。这里需要解释一下,@WebFilter这个注解并没有指定执行顺序的属性,其执行顺序依赖于Filter的名称,是根据Filter类名(注意不是配置的filter的名字)的字母顺序倒序排列,并且@WebFilter指定的过滤器优先级都高于FilterRegistrationBean配置的过滤器。

    拦截器的配置

    我们使用拦截器来实现上面同样的功能,记录请求的执行时间。首先我们实现拦截器类:

    public class MyInterceptor implements HandlerInterceptor {
        private static Long startTime;
        private static Long endTime;
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            startTime = System.currentTimeMillis();
            System.out.println("interceptor: 执行前···");
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("interceptor:执行后...");
            endTime = System.currentTimeMillis();
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("interceptor:渲染完...");
        }
    }
    
    

    这里我们需要实现HandlerInterceptor这个接口,这个接口包括三个方法:

    • preHandle是请求执行前执行的;
    • postHandler是请求结束执行的,但只有preHandle方法返回true的时候才会执行;
    • afterCompletion是视图渲染完成后才执行,同样需要preHandle返回true,该方法通常用于清理资源等工作。

    实现了拦截器,我们需要增加下面配置:

    @Configuration
    public class InterceptorConfig extends WebMvcConfigurerAdapter {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyInterceptor())
                    .addPathPatterns("/*")
                    .order(1);
            super.addInterceptors(registry);
        }
    }
    

    这里我们继承了WebMVCConfigurerAdapter,重写了addInterceptors这个方法,进行拦截器的配置,主要配置项就两个,一个是指定拦截器,第二个是指定拦截的URL。

    现在我们再启动系统访问任意一个URL:

    我们通过拦截器实现了同样的功能。不过这里还要说明一点的是,其实这个实现是有问题的,因为preHandle和postHandle是两个方法,所以我们这里不得不设置一个共享变量start来存储开始值,但是这样就会存在线程安全问题。当然,我们可以通过其他方法来解决,比如通过ThreadLocal就可以很好的解决这个问题。
    虽然拦截器在很多场景下优于过滤器,但是在这种场景下,过滤器比拦截器实现起来更简单。

    参考:https://mp.weixin.qq.com/s/dD_SzVc03HxQaz3tT7RX4A

  • 相关阅读:
    Java对于私有变量“反思暴力”技术
    图形界面汇总
    【j2ee spring】27、巴巴荆楚网-整合hibernate4+spring4(2)
    .net Work Flow 4.0
    Drainage Ditches
    Google 开源项目的风格指南
    2015第53周一
    2015第52周日
    2015第52周六
    2015第52周五
  • 原文地址:https://www.cnblogs.com/hanlk/p/13198173.html
Copyright © 2020-2023  润新知