1. 用法
1.1 继承 HandlerInterceptor
至于为什么要继承这个类,下面讲解原理的时候会提到。
我们写一个简单的HelloInterceptor拦截器,输出hello
public class HelloInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("hello,i am HelloInterceptor");
return true;
}
}
1.2 创建一个配置类WebConfiguration来继承WebMvcConfigurer,如下:
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Bean
HelloInterceptor interceptor() {
return new HelloInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册拦截器HelloInterceptor,拦截所有请求,除了/test
registry.addInterceptor(interceptor()).addPathPatterns("/**").excludePathPatterns("/test");
}
}
1.3 大功告成,测试。
@RestController
public class MvcController {
@GetMapping("/test")
private String test() {
System.out.println("i am test ......");
return "test";
}
@GetMapping("/test1")
private String test1() {
System.out.println("i am test1 ......");
return "test1";
}
}
访问localhost:8080/test 输出i am test ...... 访问localhost:8080/test1 输出 i am HelloInterceptor i am test ......
2. 原理
直接看配置类中的方法。
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册拦截器HelloInterceptor,拦截所有请求,除了/test
registry.addInterceptor(interceptor()).addPathPatterns("/**").excludePathPatterns("/test");
}
点进去发现。InterceptorRegistry里面有个registrations对象是一个InterceptorRegistration类型的拦截器列表,addInterceptor(HandlerInterceptor)方法将拦截器包装成InterceptorRegistration对象并添加到registrations对象。然后还会发现里面有个getInterceptors()方法返回所有的拦截器。我们用idea搜索一下,看那些地方调用这个方法。
public class InterceptorRegistry {
private final List<InterceptorRegistration> registrations = new ArrayList<>();
public InterceptorRegistration addInterceptor(HandlerInterceptor interceptor) {
InterceptorRegistration registration = new InterceptorRegistration(interceptor);
this.registrations.add(registration);
return registration;
}
public InterceptorRegistration addWebRequestInterceptor(WebRequestInterceptor interceptor) {
WebRequestHandlerInterceptorAdapter adapted = new WebRequestHandlerInterceptorAdapter(interceptor);
InterceptorRegistration registration = new InterceptorRegistration(adapted);
this.registrations.add(registration);
return registration;
}
/**
* 返回所有注册的拦截器
*/
protected List<Object> getInterceptors() {
return this.registrations.stream()
.sorted(INTERCEPTOR_ORDER_COMPARATOR)
// 这里很关键,类型是MappedInterceptor
.map(InterceptorRegistration::getInterceptor)
.collect(Collectors.toList());
}
private static final Comparator<Object> INTERCEPTOR_ORDER_COMPARATOR =
OrderComparator.INSTANCE.withSourceProvider(object -> {
if (object instanceof InterceptorRegistration) {
return (Ordered) ((InterceptorRegistration) object)::getOrder;
}
return null;
});
}
org.springframework.web.servlet.config.annotation.InterceptorRegistration#getInterceptor(): 返回MappedInterceptor类型的拦截器,返回值yongObject接收,也就是说我们自定义的拦截器会被包装成MappedInterceptor类型,而MappedInterceptor又继承了HandlerInterceptor,还提了匹配URL的功能,便于各种自定义开发。
protected Object getInterceptor() {
if (this.includePatterns.isEmpty() && this.excludePatterns.isEmpty()) {
return this.interceptor;
}
// 拦截路径的数组,比如我们自定义的HelloInterceptor,那么这里的include=["/**"]
String[] include = StringUtils.toStringArray(this.includePatterns);
// 排除路径的数组,这里的exclude=["/test"]
String[] exclude = StringUtils.toStringArray(this.excludePatterns);
MappedInterceptor mappedInterceptor = new MappedInterceptor(include, exclude, this.interceptor);
if (this.pathMatcher != null) {
mappedInterceptor.setPathMatcher(this.pathMatcher);
}
return mappedInterceptor;
}

org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getInterceptors()调用了之前的
protected final Object[] getInterceptors(
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
if (this.interceptors == null) {
InterceptorRegistry registry = new InterceptorRegistry();
addInterceptors(registry);
registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));
registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));
// 获取所有注册过的拦截器
this.interceptors = registry.getInterceptors();
}
return this.interceptors.toArray();
}
继续搜索,看哪个地方调用这个getInterceptors()方法。

org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerMapping: 创建RequestMappingHandlerMapping
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
mapping.setOrder(0);
// 给RequestMappingHandlerMapping设置拦截器
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
···省略其他
return mapping;
}
org.springframework.web.servlet.handler.AbstractHandlerMapping#setInterceptors(Object... interceptors):抽象类,封装了RequestMappingHandlerMapping大部分方法,可以理解成是一种模板模式,其中几个重要的方法。
- initInterceptors(): 初始化拦截器,将interceptors对象里面的拦截器添加到adaptedInterceptors中,这里可以看出添加的拦截器必须是
HandlerInterceptor类型或者WebRequestInterceptor,要不然就会抛出异常。这就解答了我们自定义异常为什么要继承HandlerInterceptor。
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered, BeanNameAware {
@Nullable
private Object defaultHandler;
private UrlPathHelper urlPathHelper = new UrlPathHelper();
private PathMatcher pathMatcher = new AntPathMatcher();
private final List<Object> interceptors = new ArrayList<>();
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
public void setInterceptors(Object... interceptors) {
this.interceptors.addAll(Arrays.asList(interceptors));
}
// 适配拦截器
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
if (interceptor instanceof HandlerInterceptor) {
return (HandlerInterceptor) interceptor;
}
else if (interceptor instanceof WebRequestInterceptor) {
return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
}
else {
throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
}
}
// 初始化拦截器
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
所以看到这里,就可以知道我们自动定义的拦截器最后被添加到了AbstractHandlerMapping中。分析到这里差不多快结束了。
2.1 HandlerExecutionChain
在最后分析之前,要先了解一下这个类HandlerExecutionChain拦截器链,这是由handle与一系列的拦截器组成的,也就是我们自定义的拦截器会被放入这个类中,进行执行,话不多说,直接debug。
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler;
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex = -1;
}
在org.springframework.web.servlet.DispatcherServlet#doDispatch中打上断点,请求http://localhost:8080/test,
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 获取HandlerExecutionChain,核心地方,其他地方先略过
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 执行拦截器中的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// handle真正执行
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
// 执行拦截器中的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
我们发现返回HandlerExecutionChain的方法getHandler(processedRequest),我们知道HandlerMapping接口中只有一个方法返回HandlerExecutionChain,而它的实现类恰好是我们上面分析的AbstractHandlerMapping。
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获取handle,有兴趣的可以深入了解这里
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 本文的关注点,获取HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// 获取请求路径,比如我们的URL是localhost:8080/test 这里得到的lookupPath就是/test
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
// 遍历拦截器,判断是不是MappedInterceptor,如果是的话,则判断路径是否满足自定义的路径
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
// MappedInterceptor也就是我们自定义的拦截器,然后将路径/test与MappedInterceptor里面的excludePatterns和includePatterns进行匹配
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
org.springframework.web.servlet.handler.MappedInterceptor#matches(String ,PathMatcher):
public final class MappedInterceptor implements HandlerInterceptor {
@Nullable
private final String[] includePatterns;
@Nullable
private final String[] excludePatterns;
private final HandlerInterceptor interceptor;
@Nullable
private PathMatcher pathMatcher;
// 因为excludePatterns数组里面包含"/test",所以不匹配,返回false,这个请求中的拦截器连中就没有我们自定义的HelloInterceptor
public boolean matches(String lookupPath, PathMatcher pathMatcher) {
PathMatcher pathMatcherToUse = (this.pathMatcher != null ? this.pathMatcher : pathMatcher);
if (!ObjectUtils.isEmpty(this.excludePatterns)) {
for (String pattern : this.excludePatterns) {
if (pathMatcherToUse.match(pattern, lookupPath)) {
return false;
}
}
}
if (ObjectUtils.isEmpty(this.includePatterns)) {
return true;
}
for (String pattern : this.includePatterns) {
if (pathMatcherToUse.match(pattern, lookupPath)) {
return true;
}
}
return false;
}
}
到此拦截器的原理就介绍完了,如果文章有错误或者你有什么疑问,请留言或者通过邮箱联系我creazycoder@sina.com