比较使用Filter、Interceptor、Aspect 拦截Rest服务

以计算服务处理时间为例,分解介绍三种拦截方式在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");
    }
}

如何使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;
    }
}

拦截器特点

  • 优点: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);
    }
}

如何使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);
    }
}

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;
    }
}

切面的特点

  • 它最后拦截到方法,因为在请求到达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

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

  • 访问时拦截顺序就是Filter->Interceptor->Aspect ->Controller。
  • 目标方法执行时加入发生异常:异常的捕获顺序会加上ControllerAdvice这一层。也就是:Controller->Aspect ->ControllerAdvice->Interceptor->Filter。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值