上一篇文章分享了Springboot项目快速实现过滤器功能,本篇文章接着来盘一盘拦截器,仔细研究后会发现,其实拦截器和过滤器的功能非常类似,可以理解为面向切面编程的一种具体实现。下面就其功能特性、工作原理、涉及到的核心类以及具体的实现方式几个方面进行梳理,以便在实际业务开发过程中,可以根据实际需要选择合适的实现访求。
jdk版本:1.8
开发工具:Intellij iDEA 2020.1
springboot:2.3.9.RELEASE
拦截器,在java中只是一种概念,并没有一个具体的实现或标准,通常所说的java web的拦截器实际是指HandleInterceptor接口,这是Spring MVC提供的一套拦截机制,可以在controller处理请求之后和响应处理结果之后,对请求信息和响应结果进行拦截;但不能修改具体的请求信息和响应结果;Spring MVC的拦截器的概念和servlet的Filter非常类似,但是在执行顺序上是有所不同的,下面会重点介绍;从本质上来说,SpringMVC的拦截器机制,是AOP(面向切面编程)的一种具体实现,可以很方面用户对实际业务中公共的一些业务进行横向抽取,但是和servlet的Filter一样,有一定的局限性,如能对请求信息和响应结果进行拦截,但不能修改具体的请求信息和响应结果;能拦截controller层的方法,但是不能拦截service层的方法;
如果把过滤器和拦截器放在一起来分析其工作原理,就需要再次明确一件事:Filter接口的全限定类名是javax.servlet.Filter,HandleInterceptor接口的全限定类名是org.springframework.web.servlet.HandlerInterceptor,从这里就可以看得出来,Filter是servlet里就有的接口,HandleInterceptor是Spring中新增的接口,两个是来源完全不同的东西,但功能却很类似,那么放在一起又会发生什么奇妙的事呢?
1、过滤器1、过滤器2、拦截1、拦截2对象,会在Spring容器启动的过程中,完成bean的注册;
2、当客户端向服务端发起http请求时,在请求到达具体的controller方法之前,会先经过过滤器、拦截器的处理,其中过滤器的执行时机要早于拦截器;和过滤器一样,如果当前请求匹配到了多个拦截器,会形成一个拦截器链,按照默认或指定的优先级,依次经过各个拦截器对象的处理之后,才会到达具体的controller方法;
3、到达具体的controller方法后,开始业务处理;得到业务处理结果后,响应结果也会经过请求进来时的所有过滤器、拦截器的处理,不同的是顺序与请求进来时完全相反,即先进后出;
1、实现org.springframework.web.servlet.HandlerInterceptor接口;
2、继承 org.springframework.web.servlet.handler.HandlerInterceptorAdapter类;
HandlerInterceptor是SpringMVC提供的实现拦截器功能的一个标准接口,接口内有三个方法:
1、preHandler(HttpServletRequest request, HttpServletResponse response, Object handler) ,方法在请求处理之前会被调用。该方法在 Interceptor 类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回值是 Boolean 类型;当它返回 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当它返回为 true 时,会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候就会调用当前请求的 Controller 方法;
2、postHandler(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 方法在当前请求处理完成之后,也就是 Controller 方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作;
3、afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法需要在当前对应的 Interceptor 类的 preHandle 方法返回值为 true 时才会执行;该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行,主要用来进行资源清理或释放。
HandlerInterceptorAdapter是一个抽象类,该抽象类实现了AsyncHandlerInterceptor接口,而AsyncHandlerInterceptor接口又继承于HandlerInterceptor,因此可以认为HandlerInterceptorAdapter是实现了HandlerInterceptor接口,但是HandlerInterceptorAdapter是抽象类,实际上并没有具体实现,所以在实现拦截器功能功能的两种方式本质上是一种;
定义两个拦截器:MyInterceptor1和MyInterceptor2,通过一次完成的请求,来分析一下HandlerInterceptor接口的preHandle()、postHandle()、afterCompletion()是如何工作的?
1、定义和注册两个拦截器:MyInterceptor1和MyInterceptor2,设置拦截器的拦截路径为/person/get*,myInterceptor2的优先级高于myInterceptor1;
2、发起http请求(URL:/person/get);
3、/person/get请求在执行PersonController#getPerson()之前,会先执行到MyInterceptor1和MyInterceptor2的preHandler()方法;
4、如果MyInterceptor1和MyInterceptor2的preHandler()方法返回都为true,则会执行到PersonController#getPerson();
5、PersonController#getPerson()执行完成后,还没有返回视图渲染对象之前MyInterceptor1和MyInterceptor2的postHandle()方法触发执行;
6、再然后就是整个请求处理完之后,MyInterceptor1和MyInterceptor2的afterCompletion()方法触发执行;
PersonController.java
@Controller
@RequestMapping("/person")
@Slf4j
public class PersonController {
@Autowired
private IPersonService personService;
@GetMapping("/get")
@ResponseBody
public Person getPerson(Integer id) {
Person person = this.personService.get(id);
log.info("//查询person详情执行完成");
return person;
}
}
定义WebConfig类实现WebMvcConfigurer接口,实现addInterceptors(),完成拦截器的注册以及设置好拦截路径和优先级顺序;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor2())
.addPathPatterns("/person/get*")
.order(1);
registry.addInterceptor(new MyInterceptor1())
.addPathPatterns("/person/get*")//设置拦截请求路径;*是通配符;
.order(2)//设置拦截器对象的优先级,如果有多个拦截器对象,设置数字越小,优先级越高;
.excludePathPatterns("/test");//设置排除拦截的请求路径;
}
}
定义MyInterceptor1类实现HandlerInterceptor接口
@Slf4j
public class MyInterceptor1 implements HandlerInterceptor {
//preHandle方法在请求处理之前被调用;当返回true,则表示可以继续后续请求处理;当返回false,则表示请求结束;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("//myInterceptor1的preHandle方法开始执行");
log.info("//请求路径:{}",request.getRequestURI());
HandlerMethod handlerMethod = (HandlerMethod) handler;
log.info("//拦截类:{},方法:{}",handlerMethod.getBean().getClass().getName(),handlerMethod.getMethod().getName());
Enumeration parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()){
String parameterName = parameterNames.nextElement();
String parameterValue = request.getParameter(parameterName);
log.info("//请求参数>{}:{}",parameterName,parameterValue);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("//myInterceptor1的postHandle方法开始执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("//myInterceptor1的afterCompletion方法开始执行");
}
}
定义MyInterceptor2,继承HandlerInterceptorAdapter抽象类,重写HandlerInterceptorAdapter类的方法;
@Slf4j
public class MyInterceptor2 extends HandlerInterceptorAdapter {
//preHandle方法在请求处理之前被调用;当返回true,则表示可以继续后续请求处理;当返回false,则表示请求结束;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("//myInterceptor1的preHandle方法开始执行");
log.info("//请求路径:{}",request.getRequestURI());
HandlerMethod handlerMethod = (HandlerMethod) handler;
log.info("//拦截类:{},方法:{}",handlerMethod.getBean().getClass().getName(),handlerMethod.getMethod().getName());
Enumeration parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()){
String parameterName = parameterNames.nextElement();
String parameterValue = request.getParameter(parameterName);
log.info("//请求参数>{}:{}",parameterName,parameterValue);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("//myInterceptor1的postHandle方法开始执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.info("//myInterceptor1的afterCompletion方法开始执行");
}
结果验证
Filter与HandleInterceptor的执行顺序:在请求处理阶段,先经过Filter然后再经过HandleInterceptor,在响应处理阶段,先经过HandleInterceptor,再经过Filter,即先进后出;
拦截器的功能与Filter比较类似,实现方式也比较简单,需要特别注意的是拦截器的执行时机稍晚于过滤器。那么Spring的AOP与过滤器、拦截器相比,又有哪些不同和需要注意的事项呢?下一篇文章会把这个坑给填上。