SpringMVC:拦截器和过滤器

首先说明一下二者的区别:

  1. 拦截器基于java的反射机制,而过滤器是基于函数回调

  2. 拦截器不依赖于servlet容器,过滤器依赖servlet容器

  3. 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用

  4. 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次

  5. 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器注入一个service,可以调用业务逻辑

过滤器和拦截器之间的关系如下图,Filter包裹Servlet,Servlet包裹Interceptor

 过滤器的触发时机是容器后,servlet之前,所以过滤器的  doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) 的入参是ServletRequest,而不是HttpServletRequest,因为过滤器在HttpServlet之前

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

    System.out.println("before...");

    chain.doFilter(request, response);

    System.out.println("after...");
}

 这个chain.doFilter(request, response) 作用是将请求转发给过滤器链上下一个对象,下一个对象是指filter,如果没有filter那就是你请求的资源

 拦截器是被包裹在过滤器之中

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("preHandle");
    return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("postHandle");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("afterCompletion");
}

preHandle 这个方法是在过滤器的 chain.doFilter( request, response ) 方法的前一步执行,且在调用Controller之前调用,当返回false后,会跳过之后的拦截器,并且不会执行所有的拦截器 postHandle ,并且调用返回true的拦截器的 afterCompletion 方法

postHandle 是调用 Controller 之后被调用,但是在渲染 View 页面之前

afterCompletion 是调用完 Controller 接口,渲染 View 页面最后调用,返回true的拦截器都会调用该拦截器的 afterCompletion 方法,顺序相反。这个方法也是在过滤器返回给前端前一步执行 

 

多个过滤器和拦截器是如何配置的呢,下面结合Spring Boot写个demo,地址是 https://gitee.com/colin220/intercept

首先写一个Controller,内容如下:

@Controller
public class InterceptController {

    @RequestMapping("/index")
    public ResponseEntity<?> index() {
        System.out.println("Controller        Handler");
        return ResponseEntity.ok("index");
    }
}

这个Controller比较简单,浏览器访问 http://localhost:8080/index 会返回一个字符串 "index"。

写三个过滤器,需要实现javax.servlet.Filter接口,实现其中的三个方法。这三个过滤器分别命名为 FirstFilter SecondFilter ThirdFilter,以FirstFilter为例,其他的两个类内容和其基本一致,其代码内容如下:

import javax.servlet.*;
import java.io.IOException;

@component
public class FirstFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("FirstFilter init"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("FirstFilter doFilter pre"); filterChain.doFilter(servletRequest, servletResponse); System.out.println("FirstFilter doFilter aft"); } @Override public void destroy() { System.out.println("FirstFilter destroy"); } }

然后写一个配置类,来配置这三个过滤器,其代码内容如下:

@Configuration
public class FilterConfig {
  
  @Autowired
  private FirstFilter firstFilter;
  @Autowired
  private SecondFilter secondFilter;
  @Autowired
  private ThirdFilter thirdFilter; @Bean
public FilterRegistrationBean filterRegistrationBeanFirst() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
// 可以采用 spring 的依赖注入 20191117update filterRegistrationBean.setFilter(firstFilter
); filterRegistrationBean.addUrlPatterns("/index/*"); filterRegistrationBean.setName("FirstFilter"); filterRegistrationBean.setOrder(1); return filterRegistrationBean; } @Bean public FilterRegistrationBean filterRegistrationBeanSecond() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new SecondFilter()); filterRegistrationBean.addUrlPatterns("/index/*"); filterRegistrationBean.setName("SecondFilter"); filterRegistrationBean.setOrder(2); return filterRegistrationBean; } @Bean public FilterRegistrationBean filterRegistrationBeanThird() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new ThirdFilter()); filterRegistrationBean.addUrlPatterns("/index/*"); filterRegistrationBean.setName("ThirdFilter"); filterRegistrationBean.setOrder(3); return filterRegistrationBean; } }

因为有三个Filter,因此需要三个org.springframework.boot.web.servlet.FilterRegistrationBean,每个FilterRegisterationBean中需要设置属性 filterRegistrationBean.setFilter(new FirstFilter( )) ,并设置这个Filter拦截的url,顺序order等属性。

 

接下来写三个拦截器,需要实现 org.springframework.web.servlet.HandlerInterceptor 接口,重写其中的三个方法,这三个拦截器分别命名为FirstInterceptor SecondInterceptor ThirdInterceptor,以FirstInterceptor为例,其他两个类内容基本一致,其代码内容如下:

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 FirstInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 在请求处理之前进行调用(Controller方法调用之前) System.out.println("FirstInterceptor preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后 ) System.out.println("FirstInterceptor postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作) System.out.println("FirstInterceptor afterCompletion"); } }

然后写一个配置类,来配置这三个拦截器。这个配置类要实现org.springframework.web.servlet.config.annotation.WebMvcConfigurer。因为WebMvcConfigurerAdapter 在Spring5.0已被废弃,所以采用WebMvcConfigurer,其代码内容如下:

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

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
   @Autowired
private FirstInterceptor firstInterceptor;
@Autowired
private SecondInterceptor secondInterceptor;
@Autowired
private ThirdInterceptor thirdInterceptor;
     @Override
public void addInterceptors(InterceptorRegistry registry) {
     // 可以将 FirstInterceptor 之类的由 spring 进行管理 不建议采用 new 的方法创建对象 20191117update registry.addInterceptor(firstInterceptor
).addPathPatterns("/index/**"); registry.addInterceptor(secondInterceptor).addPathPatterns("/index/**"); registry.addInterceptor(thirdInterceptor).addPathPatterns("/index/**"); } }

其中InterceptorRegistry内部有个List,这个addInterceptor( )方法是用来存放添加进去的interceptor,按照添加的先后顺序,依次拦截。 

这样就完成了多个过滤器和多个拦截器的配置,浏览器访问 http://localhost:8080/index 可以看到控制台打印内容如下:

posted @ 2019-02-28 14:05  colin220  阅读(4095)  评论(1编辑  收藏  举报