美文网首页Spring-Boot
过滤器(filter)/拦截器(Interceptor)/切片(

过滤器(filter)/拦截器(Interceptor)/切片(

作者: 董二弯 | 来源:发表于2019-03-13 11:00 被阅读5次

    Restful API 的拦截

    在某些情况下,会有需求对api拦截做一些统一的业务处理。简单的如api执行时间等。下面用过滤器、拦截器、切片三种方式实现统计api执行时间功能,从中介绍三者的区别。

    过滤器(Filter)实现:

    @Component

    public class TimeFilter implements Filter {

       @Override

       public void init(FilterConfig filterConfig) throws ServletException {

       }

       @Override

       public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

           //开始时间

           long start = System.currentTimeMillis();

           //执行下一个过滤器。无执行Api

           filterChain.doFilter(servletRequest, servletResponse);

           System.out.println("执行时间为:"+(System.currentTimeMillis()-start));

       }

       @Override

       public void destroy() {

       }

    }

    有时我们需要使用第三方的过滤器,在第三方的过滤器中没有@Component注解,也就是不能自动注册成bean。在springboot项目中可通过配置注解解决问题。如下:

    @Configuration

    public class WebConfig{

       @Bean

       public FilterRegistrationBean timeFilter(){

           FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();

           TimeFilter timeFilter = new TimeFilter();

           filterRegistrationBean.setFilter(timeFilter);

           List<String> urls = new ArrayList<>(4);

           urls.add("/*");

           filterRegistrationBean.setUrlPatterns(urls);

           return filterRegistrationBean;

       }

    }

    Filter只能拦截servlet请求响应,不能知道是那个控制器执行的那个API。若要获取可使用Interceptor拦截器。

    拦截器(Interceptor)实现

    拦截器为spring框架本身提供。

    @Component

    public class TimeInterceptor implements HandlerInterceptor {

       @Override

       public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {

           System.out.println("pre");

           System.out.println(((HandlerMethod)o).getBean().getClass().getName());

           System.out.println(((HandlerMethod)o).getMethod().getName());

           httpServletRequest.setAttribute("startTime",System.currentTimeMillis());

           return true;

       }

       @Override

       public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

           System.out.println("postHandle");

           long startTime = (long) httpServletRequest.getAttribute("startTime");

           System.out.println("耗时:"+(System.currentTimeMillis()-startTime));

       }

       @Override

       public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

           System.out.println("afterCompletion");

           long startTime = (long) httpServletRequest.getAttribute("startTime");

           System.out.println("耗时:"+(System.currentTimeMillis()-startTime));

           System.out.println("ex is "+e);

       }

    }

    在执行控制器方法前执行拦截器preHandle方法,然后执行控制器方法,控制器方法执行完毕后执行postHandle方法。最后执行afterCompletion方法。若控制器方法抛错则不会执行postHandle方法,但会执行afterCompletion方法。在方法的最后一个object类型参数中可获取控制器api信息。

    和filter不同,在注册成bean后。拦截器需要额外的配置生效,如下:

    @Configuration

    public class WebConfig extends WebMvcConfigurerAdapter {

       @Autowired

       private TimeInterceptor timeInterceptor;

       @Override

       public void addInterceptors(InterceptorRegistry registry) {

           registry.addInterceptor(timeInterceptor);

       }

    }

    拦截器的缺陷在于无法拿到控制器方法中参数的值,若要获取到,可使用切片。

    切片(Aspect)实现

    @Aspect

    @Component

    public class TimeAspect {

       @Around("execution(* com.imooc.web.controller.UserController.*(..))")

             //表示拦UserController下所有方法

       public Object handlerControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable {

           System.out.println("切片");

           //获取方法参数

           Object[] args = joinPoint.getArgs();

           for (Object arg : args) {

               System.out.println("参数:"+arg);

           }

           long start = System.currentTimeMillis();

           Object o = joinPoint.proceed();

           System.out.println("耗时:" + (System.currentTimeMillis() - start));

           return o;

       }

    }

    总结

    过滤器能获取原始的http请求和响应的消息,但不能拿到真正处理请求方法的信息。

    拦截器能获取原始的http请求和响应的消息,也能拿到真正处理请求方法的信息,但不能拿到方法的参数。

    切片可以拿到方法的参数,但不能拿到原始的http请求和响应的消息。

    三种拦截的顺序:

    filter>interceptor>controllerAdvice>aspect>controller方法

    当controller方法方法抛出错误时,获取错误的顺序:

    aspect>controllerAdvice>interceptor>filter

    相关文章

      网友评论

        本文标题:过滤器(filter)/拦截器(Interceptor)/切片(

        本文链接:https://www.haomeiwen.com/subject/mteruqtx.html