使用切片拦截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是全局异常处理器
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。