springboot:多个拦截器的配置

x33g5p2x  于2021-10-16 转载在 Spring  
字(7.4k)|赞(0)|评价(0)|浏览(962)

springboot中实现拦截器的俩种方式,分别为实现HandlerInterceptor接口和使用servletfilter拦截器

一、实现HandlerInterceptor接口

1、创建俩个类实现HandlerInterceptor接口

自定义拦截器HandlerInterceptor会实现三个方法

  • preHandle:调用Controller某个方法之前
  • postHandle:Controller之后调用,视图渲染之前,如果控制器Controller出现了异常,则不会执行此方法
  • afterCompletion:不管有没有异常,这个afterCompletion都会被调用,用于资源清理
    OneInterceptor.java
package com.mye.hl18springbootinterceptor.intercept;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

/** * 拦截器一 */
@Component
public class OneInterceptor implements HandlerInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(OneInterceptor.class.getName());

 /** * 预处理回调方法,实现处理器预处理 * 返回值:true表示继续流程;false表示流程中断,不会继续调用其他的拦截器或者处理器 */
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object o) throws Exception {
        String url = String.valueOf(request.getRequestURL());
        LOGGER.info("1、url==" + url);
        // 放开拦截
        return true;
    }

 /** * 后处理回调方法,实现处理器(controller)的后处理,但在渲染视图之前 * 此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理 */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception {
        LOGGER.info("1、postHandle");
    }

 /** * 整个请求处理完毕回调方法,即在视图渲染完毕时回调, * 如性能监控中我们可以在此记录结束时间并输出消耗时间, * 还可以进行一些资源清理,类似于try-catch-finally中的finally, * 但仅调用处理器执行链中 */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {
        LOGGER.info("1、afterCompletion");
    }
}

TwoInterceptor.java

package com.mye.hl18springbootinterceptor.intercept;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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

/** * 拦截器二 */
@Component
public class TwoInterceptor implements HandlerInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(TwoInterceptor.class.getName());

 /** * 预处理回调方法,实现处理器预处理 * 返回值:true表示继续流程;false表示流程中断,不会继续调用其他的拦截器或者处理器 */
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object o) throws Exception {
        String url = String.valueOf(request.getRequestURL());
        LOGGER.info("2、url==" + url);
        // 放开拦截
        return true;
    }

 /** * 后处理回调方法,实现处理器(controller)的后处理,但在渲染视图之前 * 此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理 */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception {
        LOGGER.info("2、postHandle");
    }

 /** * 整个请求处理完毕回调方法,即在视图渲染完毕时回调, * 如性能监控中我们可以在此记录结束时间并输出消耗时间, * 还可以进行一些资源清理,类似于try-catch-finally中的finally, * 但仅调用处理器执行链中 */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {
        LOGGER.info("2、afterCompletion");
    }
}

2、Web配置文件中注入拦截器

Spring Boot1.0的时候可以通过继承WebMvcConfigurerAdapter完成,但在Spring Boot2.0中这个适配类是被弃用了的,所以我们可以直接实现WebMvcConfigurer来完成拦截器的添加

在实现WebMvcConfigurer之后可以选择你要重写的方法,这里重写addInterceptors这个方法来添加自定义的拦截器。

addInterceptor用于添加你自定义的拦截器实例
*
addPathPatterns用于添加要拦截的url,可以写多个。
*
excludePathPatterns用于添加不需要拦截的url,可以写多个。

package com.mye.hl18springbootinterceptor.config;

import com.mye.hl18springbootinterceptor.intercept.OneInterceptor;
import com.mye.hl18springbootinterceptor.intercept.TwoInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/** * Web配置文件 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private OneInterceptor oneInterceptor;

    @Autowired
    private TwoInterceptor twoInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 拦截所有路径 多个拦截器组成一个拦截器链
        // 注册自定义两个拦截器
        // addPathPatterns 用来添加拦截规则,/** 表示拦截所有请求 /*/表示拦截目录
        // excludePatterns 用户排除拦截
        //拦截器的拦截顺序,是按照Web配置文件中注入拦截器的顺序执行的
        registry.addInterceptor(oneInterceptor).addPathPatterns("/**");
        registry.addInterceptor(twoInterceptor).addPathPatterns("/**");

// registry.addInterceptor(oneInterceptor).addPathPatterns("/**")
// .excludePathPatterns("/stuInfo/getAllStuInfoA","/account/register");

    }
}

3、controller控制层

@RestController
public class InterceptController {

    @RequestMapping("/reqUrl")
    public String reqUrl() {
        return "success";
    }
}

4、测试结果

正常的拦截器执行顺序

总结:

拦截器1放行,拦截器2 preHandle才会执行。

拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。

只要有一个拦截器不放行,postHandle不会执行。

5、疑难解惑

excludePathPatterns失效的问题

这个现象就是你在excludePathPatterns方法中添加了要忽略的路径,但当你访问此路径的时候拦截器依然进行了拦截。

这是因为你要忽略的路径在项目中并不存在,springboot会将路径编程/error,从而无法进行排除
静态资源被拦截的问题

在Spring Boot1.0中,我们自定义的拦截器并不会对静态资源做出拦截,但是在Spring Boot2.0中,我们自定义的拦截器对静态资源同样做出了拦截

解决方法:

因为自定义的拦截器拦截了所有的路径,所以首先我们需要重写addResourceHandlers()方法,指定静态资源的访问路径前缀以及静态资源所处路径:

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //第一个方法设置访问路径前缀,第二个方法设置资源路径
    registry.addResourceHandler("/resources/**","/public/**")
        .addResourceLocations("classpath:/resources/","classpath:/public/");
}

然后在添加自定义拦截器时忽略静态资源的路径前缀:

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new LoginInterceptor())
        .addPathPatterns("/**")
        .excludePathPatterns("/","user/login","/index.html","/error.html")
        .excludePathPatterns("/public/**","/resources/**");

}

最后,在访问静态资源的时候,加上资源所处的完整路径,例如

Spring boot1.0可以这样访问静态资源:

localhost:8080/11.png

那么Spring Boot2.0加上自定义拦截器就得这样了:

localhost:8080/public/11.pnglocalhost:8080/resources/11.png

二、使用servlet的filter拦截器

import java.io.IOException;
import java.util.Arrays;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;

@Component
@WebFilter(urlPatterns="/**",filterName="loginFilter")
public class LoginFilter implements Filter{

    //排除不拦截的url
    private static final String[] excludePathPatterns = { "/stuInfo/getAllStuInfoA"};

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse res = (HttpServletResponse)response;

        // 获取请求url地址,不拦截excludePathPatterns中的url
        String url = req.getRequestURI();
        if (Arrays.asList(excludePathPatterns).contains(url)) {
            //放行,相当于第一种方法中LoginInterceptor返回值为true
            chain.doFilter(request, response);
        }

        System.out.println("开始拦截了................");
        //业务代码
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }

}

俩种方式的区别

Filter和Interceptor二者都是AOP编程思想的体现,功能基本都可以实现
拦截器功能更强大些,Filter能做的事情它都能做
Filter在只在Servlet前后起作用,而Interceptor能够深入到方法前后、异常抛出前后等
依赖于Servlet容器既web应用中,而Interceptor不依赖于Servlet容器所以可以运行在多种环境
在接口调用的生命周期,Interceptor可以被多次调用,而Filter只能在容器初始化时调用一次。
Filter和Interceptor的执行顺序
过滤前->拦截前->action执行->拦截后->过滤后

相关文章