【Spring Security OAuth2笔记系列】- 【使用Spring MVC开发RESTful API】 使用切片拦截rest服务

使用切片拦截rest服务

本节内容

  • 过滤器(Filter)
  • 拦截器(interceptor)
  • 切片(Aspect)

假设一个需求:打印出所有请求的耗时时间

Filter

  • 实现一个javax.servlet.Filter
  • @Component 让这个实现类被spring容器接管

就可以让过滤器生效了

package com.example.demo.web.filter;

import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;

/**
 * ${desc}
 * @author zhuqiang
 * @version 1.0.1 2018/8/2 14:42
 * @date 2018/8/2 14:42
 * @since 1.0
 */
@Component  // 生效需要让spring容器接管
public class TimeFilter implements Filter {
    // 初始化
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("TimeFilter init");
    }

    // 执行
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        Instant start = Instant.now();
        chain.doFilter(request, response);
        System.out.println("耗时:" + Duration.between(start, Instant.now()).toMillis());
    }

    // 销毁
    @Override
    public void destroy() {
        System.out.println("TimeFilter destroy");
    }
}

编码注册过滤器

传统的过滤器可以使用 web.xml 等方式注册,spring boot 里面可以通过配置类添加

这种方式可以把一些第三方的过滤器添加进来

package com.example.demo.web.config;

import com.example.demo.web.filter.TimeFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;

@Configuration  // 这里的注解别用错了
public class WebConfig {
    @Bean
    public FilterRegistrationBean timeFilter() {
        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new TimeFilter());
        // 可以自定义拦截路径
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }
}

过滤器有一些限制,比如获取不到具体是哪一个方法处理的;

过滤器是j2ee的规范,在拦截器之前,还没有进入我们的具体控制器方法的时候被调用

拦截器(interceptor)

  • 实现HandlerInterceptor拦截器
  • 添加到spring kvc中

实现拦截器

@Configuration
public class TimeInterceptor implements HandlerInterceptor {
    // 进入方法前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setAttribute("startTime", Instant.now());
        HandlerMethod method = (HandlerMethod) handler;
        System.out.println("preHandle " + method.getBean().getClass().getName());
        System.out.println("preHandle " + method.getMethodParameters());
        System.out.println("preHandle");
        return true;
    }

    // 进入方法后
    // 如果方法异常,则不会进入该节点
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        Instant startTime = (Instant) request.getAttribute("startTime");
        System.out.println("postHandle 耗时" + Duration.between(startTime, Instant.now()).toMillis());
    }

    // 请求后:无论如何都会走该节点
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        System.out.println("afterCompletion");
        // 注意这里的异常,如果异常被全局异常处理器ControllerExceptionHandler消费掉了的话,这里的异常信息的null
        System.out.println("afterCompletion ex" + ex);
    }
}

添加到spring mvc中: 这个不同于过滤器的添加逻辑,需要手动进行配置

@Configuration
// org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter 5.0+已过时
// 使用了jdk8 的接口默认方法
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private TimeInterceptor timeInterceptor;

    @Bean
    public FilterRegistrationBean timeFilter() {
        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new TimeFilter());
        // 可以自定义拦截路径
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 可以添加多个不同的拦截器
        registry.addInterceptor(timeInterceptor);
    }
}

切片(Aspect)

提供了更为强大的拦截功能,不只是能拦截mvc的handler;还能拦截符合截点的所有方法或则类

  • 切入点(注解)
    • 在哪些方法上起作用
    • 在什么时候起作用
  • 增强(方法)
    • 起作用时执行的业务逻辑

Aspect 注解属于aop包
`compile(“org.springframework.boot:spring-boot-starter-aop”)

官网能查看表切片怎么使用 和表达式是什么意思
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop

@Component
@Aspect
public class TimeAspect {
    // 环绕通知 还有其他类型的注解
    // 这里的表达式在官网可以学习怎么使用
    // https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop
    @Around("execution(* com.example.demo.web.controller.UserController.*(..))")
    public Object doAccessCheck(ProceedingJoinPoint point) throws Throwable {
        Instant start = Instant.now();
        Object proceed = point.proceed();  // 类似于调用过滤器链一样
        // 这里对于异常来说和之前的都类似,异常的话下面不会继续走了
        System.out.println("耗时:" + Duration.between(start, Instant.now()).toMillis());
        Object[] args = point.getArgs();
        for (Object arg : args) {
            System.out.println(arg);
        }
        return proceed;
    }
}

总结

  • 过滤器(Filter)
    • 能拿到最原始的http请求响应对象
    • 拿不到路径具体对于的handler
  • 拦截器(interceptor)
    • 能拿到handler
    • spring家族成员
  • 切片(Aspect)
    • 拿不到http请求响应对象
    • 可以拦截的更多:比如拦截mybatis生成的dao接口方法执行

处理顺序大概是下面这样;controllerAdvice是全局异常处理器

猜你喜欢

转载自blog.csdn.net/mr_zhuqiang/article/details/81666772