Java

SpringBoot中的Filter、Interceptor、Aspect

勤劳的小蜜蜂 · 12月11日 · 2019年 · · ·

本文主要讲解三种拦截的实现方法,以及三者的不同之处。

Filter(过滤器)

filter 有两种配置方式

  1. 继承 Filter 接口,实现方法,并且使用 @Compoent 声明为 bean
  2. 自定义 config 中,配置 bean 的方式装配,参考 https://www.beeij.com/code/java/springboot-filter

本文使用第一方式,实现记录请求用时

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

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("time filter start");
        long start = System.currentTimeMillis();
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("time filter: " + (System.currentTimeMillis() - start));
        System.out.println("time filter finish");
    }

    @Override
    public void destroy() {
        System.out.println("time filter destory");
    }
}

init() 和 destory() 都是 default 修饰的,不是必须要实现的

如果需要指定拦截的 url,请使用第二种方式

执行效果:

局限性:Filter 是 J2EE 规范中定义,和 Spring 是没有关系,所以 Filter 是不知道具体是什么 Controller 来处理请求的

Interceptor(拦截器)

Interceptor 是 Spring 中定义的拦截器,Interceptor 可以拿到处理请求的 Controller

Interceptor 配置步骤

  1. 新建自定义类实现接口 HandlerInterceptor,根据需要覆盖接口中定义的 defult 方法,记得加 @Compoent
  2. 创建配置类实现接口 WebMvcConfigurer,覆盖 addInterceptors 方法,记得配置类加 @Configuration

Interceptor 定义:

@Component
public class TimeInterceptor implements HandlerInterceptor{

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        System.out.println(((HandlerMethod)handler).getBean().getClass().getName());
        System.out.println(((HandlerMethod)handler).getMethod().getName());
        request.setAttribute("startTime", System.currentTimeMillis());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
        long startTime = (long) request.getAttribute("startTime");
        System.out.println("time interceptor 耗时:" + (System.currentTimeMillis() - startTime));
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterHandle");
        long startTime = (long) request.getAttribute("startTime");
        System.out.println("ex:" + ex);
        System.out.println("time interceptor 耗时:" + (System.currentTimeMillis() - startTime));
    }
}

配置类:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private TimeInterceptor timeInterceptor;

    String[] addPathPatterns={"/get/**"};

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(timeInterceptor)
                .addPathPatterns(addPathPatterns);;
    }
}

执行效果:

参数 Object handler 就是当前处理请求的 controller


当抛出异常的时候

  • 如果抛出异常,那么 postHandle 就不会执行,直接跳到 afterHandle
  • 不管抛不抛出异常,afterHandle 都会执行
  • afterHandle 中的 Exception ex 可以获取到抛出的异常

抛出异常效果如下:

  • 因为 interceptor 会拦截所有的 controller
  • BasicErrorController 是 spring 提供给的错误处理器

局限性:不能获取到传入参数的值

Aspect(切面)

spring 框架核心功能之一

Aspect 配置步骤

  • 新建切面类,添加注解 @Aspect、@Compoent
  • 在方法上定义切点
@Aspect
@Component
public class TimeAspect {
    @Around("execution(* com.ye.holdup.controller.UserController.*(..))")
    public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
        Object[] args = pjp.getArgs();
        for (Object arg : args) {
            System.out.println("arg is " + arg);
        }
        long start = System.currentTimeMillis();
        Object object = pjp.proceed();
        System.out.println("time aspect 耗时:" + (System.currentTimeMillis() - start));
        System.out.println("time aspect end");
        return object;
    }
}

四个切点

  • @Before():方法被调用之前
  • @After():方法成功返回之后
  • @AfterThrowing():在方法抛出异常之后
  • @Around():包围,完全覆盖前面的三种,一般开发都使用@Around

@Around(“execution(* com.ye.holdup.controller.UserController.*(..))”)

当 UserController 中任意一个方法被调用这个切片都会生效

ProceedingJoinPoint:当前拦截住的方法的一些信息

更多 AspectJ 表达式实例参见官方文档

https://docs.spring.io/spring/docs/5.2.2.RELEASE/spring-framework-reference/core.html#beans-annotation-config

ctrl+F 搜索 Declaring an Aspect,即可快速定位

局限性:Aspect 不能获取到 HTTP 请求和相应

源码下载

评论后可见此区域内容

总结

三者执行的顺序:

如果有异常,则会从异常开始一层层向上抛出

HTTP请求和响应Controller请求参数
Filter
Interceptor
Aspect
0 条回应