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使用与较复杂的拦截处理场景。
- JAVAScript柯里化、部分应用参数终极理解
- 翻转句子中单词的顺序
- 即将举行的全球区块链峰会强调东西方合作
- 区块链兄弟社区问答精选:关于51%攻击,你了解有多少?
- 编程小技巧
- use vue vuex vue-router, not use webpack
- 从尾到头打印链表
- Webpack+Vue如何导入Jquery和Jquery的第三方插件
- [Hadoop大数据]——Hive部署入门教程
- Vuex原来可以这样上手
- 《Hive编程指南》—— 读后总结
- Event(事件)的传播与冒泡
- [Hadoop大数据]——Hive数据的导入导出
- 区块链技术在电子游戏与博彩行业备受追捧 有望实现数字商品货币化
- 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 文档注释
- 反 996 有理:催程序员交代码,写不出好软件
- 一千个不用 Null 的理由!
- WebAssembly 是 Deno 的好搭档
- Chrome开发者工具的11个高级使用技巧
- 怒爬某破Hub站资源,简单4步撸个鉴黄平台!
- 审阅“史上”最烂的代码
- BeanUtils 是用 Spring 的还是 Apache 的好?
- 一看就会的mysql索引优化(真实案例)
- 【015期】JavaSE面试题(十五):网络IO流
- 算法篇:二分查找基础篇
- 算法篇:双指针之接雨水
- 因用了Insert into select语句,美女同事被开除了!
- 【原创】Java并发编程系列33 | 深入理解线程池(上)
- 算法篇:二分法之k个数之和
- 记一次循环依赖踩坑