Spring Boot:定制拦截器

这篇文章主要介绍如何在Spring Boot中使用拦截器。

Servlet 过滤器属于Servlet API,和Spring关系不大。除了使用过滤器包装web请求,Spring MVC还提供*HandlerInterceptor(拦截器)*工具。根据文档,HandlerInterceptor的功能跟过滤器类似,但拦截器提供更精细的控制能力:在request被响应之前、request被响应之后、视图渲染之前以及request全部结束之后。我们不能通过拦截器修改request内容,但是可以通过抛出异常(或者返回false)来暂停request的执行。

Spring MVC中常用的拦截器有:LocaleChangeInterceptor(用于国际化配置)ThemeChangeInterceptor。我们也可以增加自己定义的拦截器,可以参考这篇文章中提供的例子

实践

添加拦截器不仅是在WebConfiguration中定义bean,Spring Boot提供了基础类WebMvcConfigurerAdapter,我们项目中的WebConfiguration类需要继承这个类。

  1. 继承WebMvcConfigurerAdapter;
  2. 为LocaleChangeInterceptor添加@Bean定义,这仅仅是定义了一个interceptor spring bean,但是Spring boot不会自动将它加入到调用链中。
  3. 拦截器需要手动加入调用链。

修改后完整的WebConfiguration代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package site.javadu.bookpub;

import org.apache.catalina.filters.RemoteIpFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;

/**
* 作用: User: duqi Date: 2017/11/26 Time: 19:52
*/
@Configuration
public class WebApplication extends WebMvcConfigurerAdapter {

@Bean
public RemoteIpFilter remoteIpFilter() {
return new RemoteIpFilter();
}

/**
* 显示定义拦截器对应的bean
**/
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
return new LocaleChangeInterceptor();
}

/**
* 显示添加拦器
**/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}

使用mvn spring-boot:run运行程序,然后通过httpie访问http://localhost:8080/books?locale=foo,在终端看到如下错误信息。

1
2
3
4
Servlet.service() for servlet [dispatcherServlet] in context with path []
threw exception [Request processing failed; nested exception is
java.lang.UnsupportedOperationException: Cannot change HTTP accept
header - use a different locale resolution strategy] with root cause

这里发生错误并不是因为我们输入的locale是错误的,而是因为默认的locale修改策略不允许来自浏览器的请求修改。发生这样的错误说明我们之前定义的拦截器起作用了。

在Spring Boot 2.0.0.M6版本中显示,WebMvcConfigurerAdapter已经过期了,查看源码可以看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* An implementation of {@link WebMvcConfigurer} with empty methods allowing
* subclasses to override only the methods they're interested in.
*
* @author Rossen Stoyanchev
* @since 3.1
* @deprecated as of 5.0 {@link WebMvcConfigurer} has default methods (made
* possible by a Java 8 baseline) and can be implemented directly without the
* need for this adapter
*/
@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {

/**
* {@inheritDoc}
* <p>This implementation is empty.
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
}
//....省略
}

意思是在Spring 5.0引入Java 8之后,可以通过接口的默认方法来实现相同的功能,因此这个适配器类的方法在WebMvcConfigurer接口中实现默认方法即可。

分析

在我们的示例项目中,覆盖并重写了*addInterceptors(InterceptorRegistory registory)*方法,这是典型的回调函数——利用该函数的参数registry来添加自定义的拦截器。在Spring Boot的自动配置阶段,Spring Boot会扫描所有WebMvcConfigurer的实例,并顺序调用其中的回调函数——如果我们想对配置信息做逻辑上的隔离,可以在Spring Boot项目中定义多个WebMvcConfigurer的实例。