• Spring Boot之拦截器与过滤器(完整版)


    作者:liuxiaopeng

    链接:http://www.cnblogs.com/paddix

    作者:蓝精灵lx
    原文:https://blog.csdn.net/liuxiao723846/article/details/80656492

    参考以上两位作者文章链接进行实验整合,仅供学习交流

    一、拦截器与过滤器

    先理解一下AOP的概念,AOP不是一种具体的技术,而是一种编程思想。

    在面向对象编程的过程中,我们很容易通过继承、多态来解决纵向扩展。

    但是对于横向的功能,比如,在所有的service方法中开启事务,或者统一记录日志等功能,面向对象的是无法解决的。所以AOP——面向切面编程其实是面向对象编程思想的一个补充。而我们今天讲的过滤器和拦截器都属于面向切面编程的具体实现。

    过滤器(Filter)与拦截器()主要区别包括以下几个方面:

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

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

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

    二、过滤器的实现

    (1)自定义一个实现javax.servlet.Filter接口的过滤器类

    package com.test.domain;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    public class LogCostFilter implements Filter {
    
      //servlet容器初始化时 @Override
    public void init(FilterConfig filterConfig) throws ServletException {      }
      //servlet容器存在时 @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { long start = System.currentTimeMillis(); filterChain.doFilter(servletRequest,servletResponse); System.out.println("Execute cost="+(System.currentTimeMillis()-start)); }
      //servlet容器销毁时 @Override
    public void destroy() { } }

    这段代码的逻辑比较简单,就是在方法执行前先记录时间戳,然后通过过滤器链完成请求的执行,在返回结果之间计算执行的时间。这里需要主要,这个类必须继承Filter类。

    可以看出,Filter的生命周期由Servlet容器相关

     (2)在Spring boot中,我们需要FilterRegistrationBean来完成配置。其实现过程如下:

    package com.test.config;
    import com.test.domain.LogCostFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration
    public class FilterConfig { @Bean public FilterRegistrationBean registration(){ FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new LogCostFilter());//实例化Filter类 filterRegistrationBean.addUrlPatterns("/*");//设置匹配模式,这里设置为所有,可以按需求设置为"/hello"等等 filterRegistrationBean.setName("LogCostFilter");//设置过滤器名称 filterRegistrationBean.setOrder(1);//设置执行顺序 return filterRegistrationBean; } }

     启动服务器访问任意URL皆可得到打印的时间戳

    过滤器的另一种实现方法:可以在启动类上使用 @ServletComponentScan("com.test.domian.FilterCostConfig")和实现了Filter接口的自定义类上使用注解 @WebFilter(urlPatterns = "/*",filterName = "LogCostFilter"),不过@WebFilter这个注解是Servlet3.0的规范,且并没有指定执行的顺序,其执行顺序依赖于Filter的名称,是根据Filter类名(注意不是配置的filter的名字)的字母顺序倒序排列,并且@WebFilter指定的过滤器优先级都高于FilterRegistrationBean配置的过滤器。

    三、拦截器的实现

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

    (1)自定义一个实现HandlerInterceptor 接口的拦截器类:

    package com.test.interceptor;
    
    import org.springframework.lang.Nullable;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class LogcostInterceptor implements HandlerInterceptor {
        long start = System.currentTimeMillis();
        //preHandle是在请求执行前执行的
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            start = System.currentTimeMillis();
            return true;
            //返回true,postHandler和afterCompletion方法才能执行
            // 否则false为拒绝执行,起到拦截器控制作用
        }
    
        //postHandler是在请求结束之后,视图渲染之前执行的,但只有preHandle方法返回true的时候才会执行
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
            System.out.println("Interception cost="+(System.currentTimeMillis()-start));
        }
    
        //afterCompletion是视图渲染完成之后才执行,同样需要preHandle返回true,
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
            //该方法通常用于清理资源等工作
        }
    }

    这里我们需要自定义一个类来实现HandlerInterceptor这个接口,实现里面的三个方法,具体说明已写在方法注释中

    (2)拦截器类的配置:

    package com.test.config;
    
    import com.test.interceptor.LogcostInterceptor;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.Ordered;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    
    @Configuration
    public class InterceptorConfig extends WebMvcConfigurationSupport {
        /**
         * 静态资源配置
         */
        /*@Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/img/**")
                    .addResourceLocations("classpath:/imgs/");
    
            super.addResourceHandlers(registry);
        }*/
    
        /**
         * 默认首页配置
         */
    //    @Override
    //    public void addViewControllers(ViewControllerRegistry registry) {
    //        registry.addViewController("/").setViewName("forward:/index");
    //        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
    //        super.addViewControllers(registry);
    //    }
    
        /**
         * interceptor配置
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LogcostInterceptor())
                    //添加需要验证登录用户操作权限的请求
                    .addPathPatterns("/**")
                    //这里add为“/**”,下面的exclude才起作用,且不管controller层是否有匹配客户端请求,拦截器都起作用拦截
    //                .addPathPatterns("/hello")
                    //如果add为具体的匹配如“/hello”,下面的exclude不起作用,且controller层不匹配客户端请求时拦截器不起作用
    
                    //排除不需要验证登录用户操作权限的请求
                    .excludePathPatterns("/wang")
                    .excludePathPatterns("/css/**")
                    .excludePathPatterns("/js/**")
                    .excludePathPatterns("/images/**");
            //这里可以用registry.addInterceptor添加多个拦截器实例,后面加上匹配模式
            super.addInterceptors(registry);//最后将register往这里塞进去就可以了
        }
    }

    这里我们重写了addInterceptors这个方法,进行拦截器的配置,主要配置项就两个,一个是指定拦截器(定义方法行为),第二个是指定拦截的URL(模式匹配)。

    注释部分的静态资源配置和默认首页配置本人还未做实验,应该没问题的

    再启动系统访问任意一个URL即可得到打印的时间戳

    现在我们权限和认证更多的是在Spring Boot中整合使用shiro或者Spring Security来完成,比较少用自定义拦截器了,不过可以用拦截器实现其他功能,比如ip黑名单等等特殊场景

  • 相关阅读:
    为什么这年头蓝牙功能越来越差
    猜数字-暴力枚举
    怎么使用PHPMailer实现邮件的发送??
    实现windows操作系统和VB下Linux虚拟操作系统相互传取文件方式总结
    第一篇 对Javascript中原型的深入理解
    每天进步一点点——关于SSD写入放大问题
    两步改动CentOS主机名称
    [CentOs7]搭建ftp服务器
    Another app is currently holding the yum lock
    [CentOs7]安装mysql(2)
  • 原文地址:https://www.cnblogs.com/yifeiyaoshangtian/p/10280808.html
Copyright © 2020-2023  润新知