Spring源码学习笔记(6)——REST服务的拦截
时间:2022-07-24
本文章向大家介绍Spring源码学习笔记(6)——REST服务的拦截,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
Spring源码学习笔记(6)——REST服务的拦截
一. 拦截REST服务的几种方式
- 拦截REST服务
在很多情况下,我们需要在REST服务核心逻辑的前后,加入一些通用的额外处理,比如权限控制,日志记录和方法统计等。这时,我们可以对REST服务进行拦截,并织入我们的通用逻辑。拦截REST服务的方式有一下几种:
- Filter:过滤器
- Interceptor:拦截器
- Aspect:切面
下面以记录方法执行时间为例,分别演示几种拦截方式。
二. Filter过滤器拦截
Filter是Web开发中一个十分常用的组件,一个Web应用可以注册多个Filter,它会按照配置的路径拦截Http请求,并进行相应的处理。
首先,编写TimeFilter类,实现过滤器逻辑:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/26 14:56
* @Description:
*/
public class TimeFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.err.println("TimeFilter拦截Rest服务");
long startTime = System.currentTimeMillis();
chain.doFilter(request, response);
long endTime = System.currentTimeMillis();
System.err.println("方法执行时间: " + (endTime - startTime));
}
@Override
public void destroy() {
}
}
然后,注册该TimeFilter。在传统的JavaWeb开发中,一般是通过web.xml文件来配置Filter。而基于SpringBoot开发后,SpringBoot提供了FilterRegistrationBean来注册Filter,代码如下:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/26 14:59
* @Description:Filter配置
*/
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new TimeFilter());
String[] urlPatterns = {"/*"};
filterRegistrationBean.addUrlPatterns(urlPatterns);
return filterRegistrationBean;
}
}
访问服务,可以看到控制台输出如下:
TimeFilter拦截Rest服务
方法执行时间: 66
三. Interceptor拦截
Interceptor,顾名思义,是一种拦截器,SpringMVC提供了Interceptor拦截Http访问的执行,并在Controller处理前后增加自定义的逻辑。SpringMVC推荐使用HandlerInterceptor进行拦截。
HandlerInterceptor接口包含三个方法,具体见源码:
public interface HandlerInterceptor {
/**
* 在Handler的方法执行前置处理
* @param request
* @param response
* @param handler chosen handler to execute, for type and/or instance evaluation
* @return 如果返回true,则继续执行Handler的目标方法,否则直接返回。
* @throws Exception 如果抛出异常,则目标方法无法继续执行。
*/
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
/**
* 目标方法执行后置处理
*/
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
/**
* 最终处理,无论目标方法执行成功还是失败,都会回调该方法
*/
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
首先,开发TimeInterceptor,实现HandlerInterceptor接口:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/26 15:50
* @Description:自定义Interceptor拦截器
*/
public class TimeInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.err.println("TimeInterceptor:拦截方法执行");
request.setAttribute("startTime",System.currentTimeMillis());
if (handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod)handler;
System.err.println("拦截目标对象: " + handlerMethod.getBean() + ",目标方法: " + handlerMethod.getMethod().getName());
}
System.err.println(handler);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
Long startTime = (Long)request.getAttribute("startTime");
System.err.println("方法执行时间: " + (System.currentTimeMillis() - startTime));
}
}
下面,注册TimeInterceptor:
/**
* @Auther: ZhangShenao
* @Date: 2018/9/26 16:10
* @Description:Interceptor配置
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TimeInterceptor());
}
}
访问REST服务,可看到控制台输出:
TimeInterceptor:拦截方法执行
拦截目标对象: william.security.demo.controller.UserController@5cb2fc03,目标方法: getById
public william.security.demo.dto.UserDto william.security.demo.controller.UserController.getById(long)
方法执行时间: 146
四. Aspect切面拦截
Aspect基于Spring提供的AOP功能,提供了强大的面向切面编程的支持。AOP的思想和Spring AOP的原理这里不展开叙述,仅演示下怎么使用Aspect拦截REST服务。
首先,开发Aspect切面类,并指定切入点表达式:
@Aspect
@Component
public class TimeAspect {
@Around("execution(* william.security.demo.controller.UserController.*(..))")
public Object interceptMethodRuntime(ProceedingJoinPoint joinPoint) throws Throwable {
System.err.println("TimeAspect:拦截方法执行,目标对象: " + joinPoint.getTarget() +
",目标方法: " + joinPoint.getSignature().getName() + ",方法参数: " + joinPoint.getArgs());
long startTime = System.currentTimeMillis();
Object retVal = joinPoint.proceed();
System.err.println("方法执行时间: " + (System.currentTimeMillis() - startTime));
return retVal;
}
}
这里使用了@Around环绕通知,可以在目标方法前后都织入我们自定义的处理。
访问REST服务,查看控制台:
TimeAspect:拦截方法执行,目标对象: william.security.demo.controller.UserController@3842f7e0,目标方法: getById,方法参数: [Ljava.lang.Object;@4641d47c
方法执行时间: 7
五. 总结
- 几种拦截方式的对比 以上介绍的几种拦截REST服务的方法,各有优劣,适合于不同的应用场景。现简单进行对比: Filter拦截 Interceptor拦截 Aspect拦截 可获取到的信息 HttpRequest、HttpResponse HttpRequest、HttpResponse、目标对象和目标方法 目标方法及参数 开发难易程度 易 较易 较难 局限 仅能对Controller的方法进行拦截,并且无法获取目标方法的信息,不易于结合Spring框架处理过多的逻辑。 仅能对Controller的方法进行拦截,可以获取目标方法信息,但无法拿到方法参数。
- 使用场景
- Filter和Interceptor适用与对Http响应进行简单拦截,并加入额外处理的场景,不适用于过于复杂的横切逻辑织入。
- Aspect使用与较复杂的拦截处理场景。
- 我也来说说.net开源
- 是时候对员工进行网络安全培训了:黑客正将目标瞄准打印机
- 进度条ProgressBar
- Microsoft Visual Studio International Pack
- 柯洁5冠在手“食言”再战AI:我已看开 输赢无所谓
- JGulp: 利用Gulp 配置的前端项目自动化工作流
- 微软Enterprise Library 4.0将支持依赖注入
- 时钟AnalogClock与DigitalClock
- 细数那些在2017年被黑客滥用的系统管理工具和协议
- Compass: 在你的应用中集成搜索功能
- 列表选择Spinner
- 巧用CSS3 :target 伪类制作Dropdown下拉菜单(无JS)
- 开源的虚拟机软件 VirtualBox v1.5.2
- Farseer:一个用于Silverlight和XNA的开源物理引擎
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Kettle构建Hadoop ETL实践(一):ETL与Kettle
- 初识ABP vNext(2):ABP启动模板
- 维度爆炸?Python实现数据压缩竟如此简单!
- 如何扩展单个Prometheus实现近万Kubernetes集群监控?
- 一文带你彻底厘清 Isito 中的证书工作机制
- 如何将第三方服务注册集成到 Istio ?
- 【Pod Terminating原因追踪系列】之 containerd 中被漏掉的 runc 错误信息
- 【Pod Terminating原因追踪系列之二】exec连接未关闭导致的事件阻塞
- CD+服务网格灰度发布实践,一文带你体验如何编排更灵活
- 花十分钟的时间武装你的代码库
- 对HTML-input的一些思考和理解
- 【投稿】刀哥:Rust学习笔记 1
- 【Rust日报】2020-08-13 关于群集(Bevy)引擎ECS框架中system的语法糖是怎么实现的
- 最新情报:所有的递归都可以改写成非递归?
- 算法篇:树之转换为二叉搜索树