【二十一】springboot整合拦截器实战并对比过滤器

        本章和上章相比,通过使用拦截器的方式去模拟处理token的校验,再之后通过两者的对比,比较一下两者的不同。下面开始拦截器的基础用法学习。


springboot篇章整体栏目: 


【一】springboot整合swagger(超详细

【二】springboot整合swagger(自定义)(超详细)

【三】springboot整合token(超详细)

【四】springboot整合mybatis-plus(超详细)(上)

【五】springboot整合mybatis-plus(超详细)(下)

【六】springboot整合自定义全局异常处理

【七】springboot整合redis(超详细)

【八】springboot整合AOP实现日志操作(超详细)

【九】springboot整合定时任务(超详细)

【十】springboot整合redis实现启动服务即将热点数据保存在全局以及redis(超详细)

【十一】springboot整合quartz实现定时任务优化(超详细)

【十二】springboot整合线程池解决高并发(超详细,保你理解)

【十三】springboot整合异步调用并获取返回值(超详细)

【十四】springboot整合WebService(超详细)

【十五】springboot整合WebService(关于传参数)(超详细)

【十六】springboot整合WebSocket(超详细)

【十七】springboot整合WebSocket实现聊天室(超详细)

【十八】springboot实现自定义全局异常处理

【十九】springboot整合ElasticSearch实战(万字篇)

【二十】springboot整合过滤器实战

【二十一】springboot整合拦截器实战并对比过滤器

【二十二】springboot整合activiti7(1) 实战演示篇

【二十三】springboot整合spring事务详解以及实战

【二十四】springboot使用EasyExcel和线程池实现多线程导入Excel数据

【二十五】springboot整合jedis和redisson布隆过滤器处理缓存穿透


目录

一、 普通接口访问

二、增加一个拦截器

三、增加三个拦截器

四、对比拦截器和过滤器


一、 普通接口访问

        新建两个测试用的接口,用于对比测试是否通过拦截器。上一章已经建好了。

        测试访问,能正常访问。

二、增加一个拦截器

        下面对接口增加拦截器处理。

        和过滤器一样,分为两步,先创建自定义拦截器,再向拦截器注册器注册自定义的拦截器即可。

        1、自定义拦截器

package com.example.demo_filter_interceptor.intercepterConfig;

import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Classname TestIntercepter
 * @Description TODO
 * @Date 2022/4/12 9:03
 * @Created by zrc
 */
//自定义拦截器,实现HandlerInterceptor接口,重写他的三个方法。他的三个方法是default关键字修饰的拥有默认实现的方法
@Component
public class TestIntercepter implements HandlerInterceptor {

    @Override
    //在将请求发送到控制器controller之前执行操作,若返回true就进入控制器,若返回false就不进入控制器了
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        System.out.println("自定义拦截器1-----开始拦截,在进入控制器之前,拦截器进行拦截该请求,拦截到的token值为:"+token);
        return null != token;
    }

    @Override
    //用于在将响应发送到客户端之前执行操作,就是控制器执行完之后返回数据时执行。
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("自定义拦截器1-----控制器执行完毕,返回数据");
    }

    @Override
    //在完成请求和响应后执行操作
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("自定义拦截器1-----响应结束");
    }

}

        自定义拦截器类,通过实现HandlerInterceptor接口并重写他的三个方法,preHandle、postHandle、afterCompletion,实现拦截控制。

  • preHandle:在将请求发送到控制器controller之前执行操作,若返回true就进入控制器,若返回false就不进入控制器了。
  • postHandle:用于在将响应发送到客户端之前执行操作,就是控制器执行完之后返回数据时执行。
  • afterCompletion:在完成请求和响应后执行操作。

        上述代码在请求抵达controller之前,对请求进行拦截并对request请求中的token进行判断,若为空就返回false,就不会进入controller里了。

        2、注册自定义拦截器

package com.example.demo_filter_interceptor.intercepterConfig;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

/**
 * @Classname TestIntercepterConfig
 * @Description TODO
 * @Date 2022/4/12 9:02
 * @Created by zrc
 */
//老版本是通过继承WebMvcConfigurerAdapter类,新版本是通过实现WebMvcConfigurer接口
@Configuration
public class TestIntercepterConfig implements WebMvcConfigurer {

    //引入自定义拦截器对象
    @Resource
    private TestIntercepter testIntercepter;

    //引入自定义拦截器对象
    @Resource
    private TestIntercepter2 testIntercepter2;

    @Override
    //重写addInterceptors方法注册拦截器
    public void addInterceptors(InterceptorRegistry registry){
        //addInterceptor方法向拦截器注册器添加拦截器,addPathPatterns方法添加拦截路径匹配规则("/**"是拦截所有),excludePathPatterns方法是设置白名单,放行哪些路径
        registry.addInterceptor(testIntercepter).addPathPatterns("/**").excludePathPatterns("/userController/*").order(1);
//        registry.addInterceptor(testIntercepter2).addPathPatterns("/**").excludePathPatterns("/userController/*").order(2);
    }


}

        拦截器的注册是通过重写WebMvcConfigurer接口的addInterceptors方法实现的,老版本的WebMvcConfigurerAdapter使用时发现已经被淘汰了,不推荐使用了。

        3、演示效果

        调用测试接口,查看效果。

        带token:

        不带token: 

        在preHandle方法直接判断不通过就返回false了,不再进入controller和另外两个方法了。

三、增加三个拦截器

        增加三个拦截器来测试一下多个拦截器存在时的先后顺序执行。

        同第二节一样,新增另外两个拦截器并注册到拦截器注册器中。

package com.example.demo_filter_interceptor.intercepterConfig;

import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Classname TestIntercepter
 * @Description TODO
 * @Date 2022/4/12 9:03
 * @Created by zrc
 */
//自定义拦截器,实现HandlerInterceptor接口,重写他的三个方法。他的三个方法是default关键字修饰的拥有默认实现的方法
@Component
public class TestIntercepter2 implements HandlerInterceptor {

    @Override
    //在将请求发送到控制器controller之前执行操作,若返回true就进入控制器,若返回false就不进入控制器了
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("自定义拦截器2-----开始拦截");
        return true;
    }

    @Override
    //用于在将响应发送到客户端之前执行操作,就是控制器执行完之后返回数据时执行。
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("自定义拦截器2-----控制器执行完毕,返回数据");
    }

    @Override
    //在完成请求和响应后执行操作
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("自定义拦截器2-----响应结束");
    }

}

package com.example.demo_filter_interceptor.intercepterConfig;

import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Classname TestIntercepter
 * @Description TODO
 * @Date 2022/4/12 9:03
 * @Created by zrc
 */
//自定义拦截器,实现HandlerInterceptor接口,重写他的三个方法。他的三个方法是default关键字修饰的拥有默认实现的方法
@Component
public class TestIntercepter3 implements HandlerInterceptor {

    @Override
    //在将请求发送到控制器controller之前执行操作,若返回true就进入控制器,若返回false就不进入控制器了
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("自定义拦截器3-----开始拦截");
        return true;
    }

    @Override
    //用于在将响应发送到客户端之前执行操作,就是控制器执行完之后返回数据时执行。
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        System.out.println("自定义拦截器3-----控制器执行完毕,返回数据");
    }

    @Override
    //在完成请求和响应后执行操作
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        System.out.println("自定义拦截器3-----响应结束");
    }

}

        然后注册到注册器。

package com.example.demo_filter_interceptor.intercepterConfig;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

/**
 * @Classname TestIntercepterConfig
 * @Description TODO
 * @Date 2022/4/12 9:02
 * @Created by zrc
 */
//老版本是通过继承WebMvcConfigurerAdapter类,新版本是通过实现WebMvcConfigurer接口
@Configuration
public class TestIntercepterConfig implements WebMvcConfigurer {

    //引入自定义拦截器对象
    @Resource
    private TestIntercepter testIntercepter;

    //引入自定义拦截器对象
    @Resource
    private TestIntercepter2 testIntercepter2;

    //引入自定义拦截器对象
    @Resource
    private TestIntercepter3 testIntercepter3;

    @Override
    //重写addInterceptors方法注册拦截器
    public void addInterceptors(InterceptorRegistry registry){
        // addInterceptor方法向拦截器注册器添加拦截器,addPathPatterns方法添加拦截路径匹配规则("/**"是拦截所有),excludePathPatterns方法是设置白名单,放行哪些路径
        // 对于order的顺序:
        // 拦截器的preHandle方法是顺序执行,
        // postHandle和afterCompletion方法是倒叙执行。
        registry.addInterceptor(testIntercepter).addPathPatterns("/**").excludePathPatterns("/userController/*").order(1);
        registry.addInterceptor(testIntercepter2).addPathPatterns("/**").excludePathPatterns("/userController/*").order(2);
        registry.addInterceptor(testIntercepter3).addPathPatterns("/**").excludePathPatterns("/userController/*").order(3);
    }

}

        对于order的顺序:拦截器的preHandle方法是根据order的大小从小到大顺序执行,postHandle和afterCompletion方法是根据order的大小从小到大倒叙执行。

        演示结果如下:

四、对比拦截器和过滤器

        在这一节通过demo的方式学习一下两者的区别。

        1、拦截器是spring里面的,归于spring管理,所有他可以引入spring管理的其他bean直接使用,而过滤器不行,如下:

        新增一个拿来测试的归spring管理的service

        改造拦截器1。

@Override
    //在将请求发送到控制器controller之前执行操作,若返回true就进入控制器,若返回false就不进入控制器了
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //相比过滤器,拦截器可以在方法内使用反射机制获取目标接口上的信息,例如控制器和方法等信息。但是拿不到入参的参数列表,需要aop切面编程才行。
//        HandlerMethod handlerMethod = (HandlerMethod)handler;
//        System.out.println(Arrays.toString(handlerMethod.getMethodParameters()));
        String token = request.getHeader("token");
        if(null!=token){
            if(!token.equals(testServlet.test1())){
                System.out.println("token不正确!");
                return false;
            }else {
                System.out.println("自定义拦截器1-----开始拦截,在进入控制器之前,拦截器进行拦截该请求,拦截到的token值为:"+token);
                return true;
            }
        }else {
            return false;
        }
    }

        模拟一个获取token的正确与否的判断,运行发现没问题。

        若在过滤器中进行该操作,如下:

//doFilter()方法有多个参数,其中
    //参数request和response为Web服务器或Filter链中的上一个Filter传递过来的请求和响应对象;
    //参数chain代表当前Filter链的对象,
    //只有在当前Filter对象中的doFilter()方法内部需要调用FilterChain对象的doFilter()法才能把请求交付给Filter链中的下一个Filter或者目标程序处理
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        //这里为了使用getHeader方法获取token,转型成HttpServletRequest
        System.out.println("token:"+req.getHeader("token"));
        System.out.println(servletRequest.getParameter("id"));
        String token = req.getHeader("token");
        //再判断token是否正确
        if(null==token){
            throw new RuntimeException("token为空");
        }
        else{
            if(!testServlet.test1().equals(token)){
                throw new RuntimeException("token不正确!");
            }
        }
        //调用doFilter方法,正常返回servletResponse
        filterChain.doFilter(servletRequest, servletResponse);
    }

         运行,结果:

        testServlet会报空指针,网上有多种说法,有的说是过滤器的加载时间早于spring容器,导致加载过滤器后,bean对象还是空的;有的说是因为过滤器属于javax.servlet下面的,不归spring容器管理。过滤器也有方法引入spring的bean对象来使用,这里就不展开了。

        2、拦截器是spring在基于反射机制实现的,过滤器是基于servlet的回调实现的。

        3、拦截器可以通过preHandle方法的入参handler获取到controller层上方法的信息,除了参数列表;而过滤器只能获取到请求路径,不能获取到controller层上方法的信息。

        改造拦截器1的preHandle方法

        测试结果:

​        而过滤器不能这样获取。

        暂时就先到这里。 



版权声明:本文为weixin_56995925原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。