当前位置:   article > 正文

比较使用Filter、Interceptor、Aspect 拦截Rest服务_拦截器只拦截 restcontroller

拦截器只拦截 restcontroller

以计算服务处理时间为例,分解介绍三种拦截方式在spring boot环境下的配置,使用,以及优劣比较

Filter

Filter代码

public class TimeFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("TimeFilter Init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        long start = new Date().getTime();
        System.out.println("TimeFilter start");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("TimeFilter end 总耗时:" + (new Date().getTime() - start));
    }

    @Override
    public void destroy() {
        System.out.println("TimeFilter destroy");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

如何使Filter生效

  1. 方式一:可以通过@Component注解直接注入到容器中,此时该拦截器会默认拦截所有请求。
  2. 方式二:使用配置类,可以方便的自定义拦截路径。
@Configuration
public class WebConfig {
    
    @Bean
    public FilterRegistrationBean timeFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        TimeFilter timeFilter = new TimeFilter();
        registrationBean.setFilter(timeFilter);
        // 自定义拦截路径到urls集合中
        List<String> urls = new ArrayList<>();
        urls.add("/*");
        registrationBean.setUrlPatterns(urls);
        return registrationBean;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

拦截器特点

  • 优点:Filter拦截的优先级最高
  • 缺点:不能知晓要拦截的对象,以及方法信息。

Interceptor

Interceptor代码

public class TimeInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("TimeInterceptor preHandle");
        // 打印参数对象
        System.out.println("TimeInterceptor " + o.getClass().getName());
        // 将参数对象转成HandlerMethod对象即可获得拦截对象
        System.out.println("TimeInterceptor " + ((HandlerMethod)o).getBean().getClass().getName());
        // 获得拦截对象的方法名称
        System.out.println("TimeInterceptor " + ((HandlerMethod)o).getMethod().getName());
        httpServletRequest.setAttribute("startTime", new Date().getTime());
        // 这里决定是否放行
        return true;
    }

    /**
     * 正常返回之后进入该方法
     */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        long startTime = (long) httpServletRequest.getAttribute("startTime");
        System.out.println("TimeInterceptor耗时:" +
                (new Date().getTime() - startTime));
    }

    /**
     * 无论是否正常返回都会执行该对象
     */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("TimeInterceptor afterCompletion");
        System.out.println("TimeInterceptor exception:" + e);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

如何使Interceptor生效

只使用@Component注解将Interceptor注入到容器中并不能使拦截器生效。

  1. 使配置类继承WebMvcConfigurerAdapter类(spring boot2.0之后这个类过时,原因是2.0使用jdk8编写,接口中可以直接写default方法体,不再需要这种适配类。如果你使用2.0之后版本直接实现WebMvcConfigurer这个接口即可)
  2. 重写其addInterceptors方法
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        TimeInterceptor interceptor = new TimeInterceptor();
        registry.addInterceptor(interceptor);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Interceptor特点

  • 它的拦截顺序在Filter之后
  • 优点:Interceptor可以获取拦截对象以及拦截方法
  • 缺点:不能拦截到方法参数的具体值,因为在springmvc中,请求参数的封装实在preHandle之后进行的

Aspect

代码

@Aspect
@Component
public class TimeAspect {

    @Around("execution(* cn.net.zhipeng.web.controller.*.*(..))")
    public Object timeAspect(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long start = new Date().getTime();
        System.out.println("TimeAspect Start");
        for (Object arg : proceedingJoinPoint.getArgs()) {
            System.out.println("TimeAspect arg: " + arg);
        }
        Object proceed = proceedingJoinPoint.proceed();
        System.out.println("TimeAspect End 耗时:" + (new Date().getTime() - start));
        return proceed;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

切面的特点

  • 它最后拦截到方法,因为在请求到达Controller时Aspect会生成它的的代理对象执行方法。这可以通过在拦截器处对目标类名称的打印验证。
  • 优点:它可以获取到目标方法参数的具体值
  • 缺点:无法获取到request和response对象

关于三种拦截方式的执行顺序

我们同时开启三种拦截,观察我们之前写的打印语句。

TimeFilter start
TimeInterceptor preHandle
TimeInterceptor org.springframework.web.method.HandlerMethod
TimeInterceptor cn.net.zhipeng.web.controller.UserController$$EnhancerBySpringCGLIB$$fad54f7
TimeInterceptor getInfo
TimeAspect Start
TimeAspect arg: 1
TimeAspect End 耗时:6
TimeInterceptor耗时:65
TimeInterceptor afterCompletion
TimeInterceptor exception:null
TimeFilter end 总耗时:86
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

可以发现执行顺序如下图:
在这里插入图片描述

  • 访问时拦截顺序就是Filter->Interceptor->Aspect ->Controller。
  • 目标方法执行时加入发生异常:异常的捕获顺序会加上ControllerAdvice这一层。也就是:Controller->Aspect ->ControllerAdvice->Interceptor->Filter。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/342131
推荐阅读
相关标签
  

闽ICP备14008679号