今天阅读springMVC HandlerMapping的源码时发现自己还漏掉了一些拦截器的部分。
这次我们直接从配置文件的解析部分入手。
InterceptorsBeanDefinitionParser类
class InterceptorsBeanDefinitionParser implements BeanDefinitionParser {
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext context) {
context.pushContainingComponent(
new CompositeComponentDefinition(element.getTagName(), context.extractSource(element)));
RuntimeBeanReference pathMatcherRef = null;
if (element.hasAttribute("path-matcher")) {
pathMatcherRef = new RuntimeBeanReference(element.getAttribute("path-matcher"));
}
List<Element> interceptors = DomUtils.getChildElementsByTagName(element, "bean", "ref", "interceptor");
for (Element interceptor : interceptors) {
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
mappedInterceptorDef.setSource(context.extractSource(interceptor));
mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
ManagedList<String> includePatterns = null;
ManagedList<String> excludePatterns = null;
Object interceptorBean;
if ("interceptor".equals(interceptor.getLocalName())) {
includePatterns = getIncludePatterns(interceptor, "mapping");
excludePatterns = getIncludePatterns(interceptor, "exclude-mapping");
Element beanElem = DomUtils.getChildElementsByTagName(interceptor, "bean", "ref").get(0);
interceptorBean = context.getDelegate().parsePropertySubElement(beanElem, null);
}
else {
interceptorBean = context.getDelegate().parsePropertySubElement(interceptor, null);
}
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, includePatterns);
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, excludePatterns);
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(2, interceptorBean);
if (pathMatcherRef != null) {
mappedInterceptorDef.getPropertyValues().add("pathMatcher", pathMatcherRef);
}
String beanName = context.getReaderContext().registerWithGeneratedName(mappedInterceptorDef);
context.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, beanName));
}
context.popAndRegisterContainingComponent();
return null;
}
private ManagedList<String> getIncludePatterns(Element interceptor, String elementName) {
List<Element> paths = DomUtils.getChildElementsByTagName(interceptor, elementName);
ManagedList<String> patterns = new ManagedList<>(paths.size());
for (Element path : paths) {
patterns.add(path.getAttribute("path"));
}
return patterns;
}
}
这个类的作用很容易理解,就是将配置转化为对应的拦截器类。但是如果注意一下,我们会发现该解析类是将其转化为MappedInterceptor类。那么接下来我们就去阅读一下MappedInterceptor类部分。
MappedInterceptor类
该类中包含如下几个属性
@Nullable
private final String[] includePatterns;
@Nullable
private final String[] excludePatterns;
private final HandlerInterceptor interceptor;
@Nullable
private PathMatcher pathMatcher;
不难看出其中比接口HandlerInterceptor多了includePattern、excludePattern和pathMatcher。这三个属性的意思我们不难猜出来。其中我们需要关注的是includePattern和excludePattern。接下来看该类中的这样一个方法。
public boolean matches(String lookupPath, PathMatcher pathMatcher) {
PathMatcher pathMatcherToUse = (this.pathMatcher != null) ? this.pathMatcher : pathMatcher;
if (this.excludePatterns != null) {
for (String pattern : this.excludePatterns) {
if (pathMatcherToUse.match(pattern, lookupPath)) {
return false;
}
}
}
if (ObjectUtils.isEmpty(this.includePatterns)) {
return true;
}
else {
for (String pattern : this.includePatterns) {
if (pathMatcherToUse.match(pattern, lookupPath)) {
return true;
}
}
return false;
}
}
仔细阅读该段代码,我们不难读出MappedInterceptor的匹配规则:(我们不妨就当返回true为拦截,返回false为不拦截)
如果路径在excludePatterns中,则不拦截。如果不在,那么若includePatterns为空,则拦截,否则在includePatterns中才拦截。
比较关键的两个地方是:
- 优先判excludePatterns
- 若includePatterns列表为空且请求不在excludePatterns的情况下全部拦截,否则只拦截includePatterns中的内容