java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Springboot实现拦截器

Springboot项目快速实现拦截器功能

作者:凡夫贩夫

上一篇文章介绍了Springboot项目如何快速实现过滤器功能,本篇文章接着来盘一盘拦截器,仔细研究后会发现,其实拦截器和过滤器的功能非常类似,可以理解为面向切面编程的一种具体实现。感兴趣的小伙伴可以参考阅读

前言

上一篇文章分享了Springboot项目快速实现过滤器功能,本篇文章接着来盘一盘拦截器,仔细研究后会发现,其实拦截器和过滤器的功能非常类似,可以理解为面向切面编程的一种具体实现。下面就其功能特性、工作原理、涉及到的核心类以及具体的实现方式几个方面进行梳理,以便在实际业务开发过程中,可以根据实际需要选择合适的实现访求。

环境配置

jdk版本:1.8

开发工具:Intellij iDEA 2020.1

springboot:2.3.9.RELEASE

HandleInterceptor介绍

拦截器,在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

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

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<String> 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<String> 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与过滤器、拦截器相比,又有哪些不同和需要注意的事项呢?下一篇文章会把这个坑给填上。

到此这篇关于Springboot项目快速实现拦截器功能的文章就介绍到这了,更多相关Springboot实现拦截器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文