问题
- 当客户端请求服务器接口时,我需要验证一下用户的登陆权限。接口少的时候,我可以在每个接口里验证,但当接口成百上千时,这种重复的工作就会显得很冗余
- 同理当客户端请求服务器接口时,我想要计算每个接口运行时花费的时间,开始时可以在代码的开始和结束计算时间,最后在返回时打印时间出来。但当接口成百上千时,这种重复的工作就会显得很冗余
解决思路
针对这种重复性问题,可以抽象出一层代理来解决
- 可以写一个抽象的类实现登陆权限验证,后边每一个Controller都继承这个抽象接口
- 还有框架自带的拦截,是在controller被调用前和后做的一个拦截,原理都是抽象出一层来解决这个重复性的问题
拦截
下面我们看看 spring boot 是怎么抽象出这层拦截
一共有三种方式,分别是 Filter,Interceptor,AOP
它们拦截的顺序
Filter -> Interception -> AOP -> 具体执行的方法 -> AOP-> @ControllerAdvice 全局异常处理-> Interception-> Filter
它们的共同点都是拦截,下边谈谈它们的不同点和场景的使用范围
- Filter 不依赖于框架,依赖于Servlet容器。网上有人说不能在该类里使用bean,经过测试后,是可以的,再要在类上加 @Component就可以被spring框架扫描到。
- Interceptor 依赖spring框架,是 AOP 的一种实现,建议优先使用Interceptor,而不是Filter。毕竟框架封装的,接口功能会更丰富一些
- AOP 是spring的一大特性,它也可以起到拦截的作用,不过主要用于拦截接口,注解,可以获取接口的参数,Interceptor,Filter都不能。
总的来说,拦截接口时,使用AOP,拦截Controller时,使用Interceptor
代码实现
下边我们看看它们的具体实现,很简单,就不一一解释了
Filter 实现
/**
* Created jixinshi on 2019-07-08.
*/
@Component
@Slf4j
public class TestFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("========= before doFilter ===========");
chain.doFilter(request, response);
log.info("========= after doFilter ===========");
}
@Override
public void destroy() {
}
}
Interceptor 实现
/**
* Created jixinshi on 2019-07-08.
* 拦截器,继承 HandlerInterceptorAdapter,可以根据需求实现接口
*/
@Component
@Slf4j
public class TestInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("========= preHandle ===========");
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("========= postHandle ===========");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
/**
* Created jixinshi on 2019-07-08.
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Resource
private TestInterceptor testInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(testInterceptor)
.addPathPatterns("/**");
}
}
AOP 实现
/**
* Created jixinshi on 2019-07-08.
*/
@Component
@Aspect
@Slf4j
public class TestAspect {
@Pointcut("execution(public * com.example.demo.controller.*Controller.*(..))")
public void pointcut() {
}
@Around("pointcut()")
public Object function(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
log.info("========= before aspect ===========");
Object proceed = proceedingJoinPoint.proceed();
log.info("========= after aspect ===========");
return proceed;
}
}
另外,之前写了一篇 Spring AOP 通过注解的方式 打印接口执行的时间
请点这里
调用接口 hello/test
执行效果图
衍生问题
拦截涉及到Spring MVC框架的前与后,既然来都来了,下篇文章就让我们思考下Controller在Spring MVC中的地位,并如何被抽象?
写完了
https://www.jianshu.com/p/058216e3b543
参考
- SpringBoot中的拦截机制
- Spring:过滤器filter、拦截器interceptor、和AOP的区别与联系
- Spring 梳理 - filter、interceptor、aop实现与区别 -第二篇
- Filter,Interceptor,AOP 区别与实例
本文是基于自己理解写的,如果有错欢迎留言讨论