<spring.version>5.2.4.RELEASE</spring.version>
贴一个几种HandlerMapping的使用
文章目录
1. 类关系依赖图与继承关系
主要看红线的,即如下继承关系:
2. AbstractHandlerMapping
还是见了很多次的模板模式,AbstractHandlerMapping设计了HandlerMapping实现的整体结构,子类只需要通过模板方法提供一些初始值或者具体的实现。
HandlerMapping的作用是根据request查找Handler和Interceptors。获取Handler的过程通过模板方法getHandlerInternal交给了子类。AbstractHandlerMapping中保存了所用配置的Interceptor,在获取到Handler后会自己根据从request提取的lookupPath将相应的Inte-rceptors装配上去,当然子类也可以通过getHandlerInternal方法设置自己的Interceptor,getHandlerInternal的返回值为Object类型。
2.1 初始化
AbstractHandlerMapping继承了WebApplicationObjectSupport,初始化时会自动调用模板方法initApplicationContext,AbstractHandlerMapping的创建就是在initApplicationContext方法里面实现的。
@Override
protected void initApplicationContext() throws BeansException { //钩子函数留给子类实现,将子类添加的拦截器加入到interceptors
extendInterceptors(this.interceptors);
//detectMappedInterceptors方法用于将Spring MVC容器及父容器中的所有MappedInter-ceptor类型的Bean添加到adaptedInterceptors属性
detectMappedInterceptors(this.adaptedInterceptors);
//initInterceptors方法的作用是初始化Interceptor,具体内容其实是将interceptors属性里所包含的对象按类型添加到adaptedInterceptors
initInterceptors();
}
/ * *
*子类可以覆盖的扩展钩子来注册额外的拦截器,
*给定配置的拦截器(参见{@link #setInterceptors})。
*
*将在{@link #initInterceptors()}调整指定的代码之前被调用
*拦截器到{@link HandlerInterceptor}实例。
*
默认实现为空。
* @param拦截器配置的拦截器列表(从不{@code null}),允许
*在现有的拦截器之前和之后添加更多的拦截器
* /
protected void extendInterceptors(List<Object> interceptors) {
}
/ * *
*检测类型为{@link MappedInterceptor}的bean,并将其添加到映射的拦截器列表中。
*
这是在任何{@link MappedInterceptor MappedInterceptors}之外被调用的,可能已经提供
*通过{@link #setInterceptors},默认情况下添加所有类型为{@link MappedInterceptor}的bean
*从当前上下文及其祖先。子类可以重写和改进这个策略。
添加{@link MappedInterceptor}实例到的空列表
* /
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}
/ * *
*初始化指定的拦截器,检查{@link MappedInterceptor}和
*适应{@link HandlerInterceptor}和{@link WebRequestInterceptor HandlerInterceptor}和
* {@link WebRequestInterceptor}如果需要的话。
* @see # setInterceptors
* @see # adaptInterceptor
* /
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));
}
}
}
5.x 没有mappedInterceptors
mappedInterceptors:此类Interceptor在使用时需要与请求的url进行匹配,只有匹配成功后才会添加到getHandler的返回值HandlerExecutionChain里。它有两种获取途径:从interceptors获取或者注册到spring的容器中通过detectMappedInterceptors方法获取。
AbstractHandlerMapping中的Interceptor有三个List类型的属性:interceptors、adaptedInterceptors;
-
Interceptors:用于配置Spring MVC的拦截器,有两种设置方式:①注册Handler-Mapping时通过属性设置;②通过子类的extendInterceptors钩子方法进行设置。Interceptors并不会直接使用,而是通过initInterceptors方法按类型分配到adaptedInterceptors中进行使用,Interceptors只用于配置。
-
adaptedInterceptors:这种类型的Interceptor不需要进行匹配,在getHandler中会全部添加到返回值HandlerExecutionChain里面。它只能从interceptors中获取。
2.2 使用
HandlerMapping是通过getHandler方法来获取处理器Handler和拦截器Interceptor的。
/ * *
*查找给定请求的处理程序,返回到默认值
*处理程序,如果没有找到特定的。
* @param请求当前HTTP请求
返回对应的处理程序实例,或默认处理程序
* @see # getHandlerInternal
* /
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//又是一个模板方法,获取Handler
Object handler = getHandlerInternal(request);
if (handler == null) {
//如果没有就使用默认的Handler
//默认的Handler保存在AbstractHandler-Mapping的一个Object类型的属性defaultHandler中
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean名称还是解析处理程序?
if (handler instanceof String) {
String handlerName = (String) handler;
//如果找到的Handler是String类型,则以它为名到Spring MVC的容器里查找相应的Bean。
handler = obtainApplicationContext().getBean(handlerName);
}
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 (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
/**
*查找给定请求的处理程序,如果没有,返回{@code null}
*找到了特定的一个。该方法由{@link #getHandler}调用;
*一个{@code null}返回值将导致默认处理程序,如果设置了一个。
对于CORS飞行前请求,此方法应该返回一个不匹配的
*飞行前的请求,但基于URL的预期实际请求
*路径,从“访问-控制-请求-方法”头的HTTP方法,和
*来自“访问-控制-请求-报头”的报头因此允许
* CORS配置通过{@link #getCorsConfiguration(Object, HttpServletRequest)}获得,
注意:此方法也可能返回预先构建的{@link HandlerExecutionChain},
*将处理程序对象与动态确定的拦截程序相结合。
*静态指定的拦截器将合并到这样一个已有的链中。
* @param请求当前HTTP请求
* @返回相应的处理程序实例,如果没有找到,则返回{@code null}
* @抛出异常,如果有内部错误
*/
@Nullable
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
接下来看:
/**
*为给定的处理程序构建{@link HandlerExecutionChain},包括
*适用的拦截器。
默认实现构建一个标准的{@link HandlerExecutionChain}
*与给定的处理程序,处理程序映射的常见拦截器,和任何
匹配当前请求URL。拦截器
*按注册顺序添加。子类可以覆盖它
*为了扩展/重新排列拦截器列表。
注意:传递进来的处理程序对象可以是原始处理程序或a
*预构建的{@link HandlerExecutionChain}。这个方法应该处理这些
*显式地使用两种情况,要么构建一个新的{@link HandlerExecutionChain}
*或扩展现有的链。
如果只是在自定义子类中添加拦截器,请考虑调用
* {@code超级。getHandlerExecutionChain(处理器,请求)}和调用
在返回的链对象上* {@link HandlerExecutionChain#addInterceptor}。
解析的处理程序实例(never {@code null})
* @param请求当前HTTP请求
* @return HandlerExecutionChain (never {@code null})
* @see # getAdaptedInterceptors ()
*/
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//提取出路径
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
//找到匹配的interceptor
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
//等于找到对应该路径的拦截器
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
//否则就是全局拦截器,加入执行链
chain.addInterceptor(interceptor);
}
}
return chain;
}
再来看看MappedInterceptor 是个什么东西:
/*
*包含并委托对{@link HandlerInterceptor}的调用
*包括(可选排除)拦截器应该应用的路径模式。
*还提供了匹配逻辑来测试拦截器是否适用于给定的请求路径。
MappedInterceptor可以直接注册到any
* {@link org.springframework.web.servlet.handler.AbstractHandlerMethodMapping}。
*此外,类型为{@code MappedInterceptor}的bean被自动检测
* {@code AbstractHandlerMethodMapping}(包括祖先ApplicationContext的)
*有效地意味着拦截器注册为“全局”的所有处理程序映射。
*/
public final class MappedInterceptor implements HandlerInterceptor {
看注释可见,这是一个特定的拦截器。
3. AbstractUrlHandlerMapping
3.1 getHandlerInternal
从名字就可以看出它是通过url来进行匹配的。此系列大致原理是将url与对应的Handler保存在一个Map中,在getHandlerInternal方法中使用url从Map中获取Handler,AbstractUrlHandlerMapping中实现了具体用url从Map中获取Handler的过程,而Map的初始化则交给了具体的子孙类去完成。
AbstractUrlHandlerMapping重写了父类的getHandlerInternal():
/ * *
*查找给定请求的URL路径的处理程序。
* @param请求当前HTTP请求
* @返回处理程序实例,如果没有找到则{@code null}
* /
@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
//我们需要直接关心默认处理程序,因为我们需要这样做
//还公开它的PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE。
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean名称还是解析的处理程序?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
//可以用来校验的Handler和request是否匹配,是模板方法
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
lookupHandler方法用于使用lookupPath从Map中查找Handler,不过很多时候并不能直接从Map中get到,因为很多Handler都是用了Pattern的匹配模式,如“/show/article/”,这里的星号可以代表任意内容而不是真正匹配url中的星号,如果Pattern中包含PathVariable也不能直接从Map中获取到。另外,一个url还可能跟多个Pattern相匹配,这时还需要选择其中最优的,所以查找过程其实并不是直接简单地从Map里获取,单独写一个方法来做也是应该的。
/**
*查找给定URL路径的处理程序实例。
*
支持直接匹配,例如已注册的"/test"匹配"/test",
*和各种ant风格的模式匹配,例如注册的“/t*”匹配
*“/测试”和“/团队”。有关详细信息,请参见AntPathMatcher类。
*
寻找最精确的模式,其中最精确的定义为
*最长路径模式。
@param urlPath bean映射到的URL
* @param请求当前HTTP请求(暴露映射到的路径)
* @返回相关的处理程序实例,如果没有找到,则返回{@code null}
* @see # exposePathWithinMapping
* @see org.springframework.util.AntPathMatcher
*/
@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 直接匹配
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean名称还是解析处理程序?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// 模式匹配 比如带*号的
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
//最佳匹配
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
//排序
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger.trace("Matching patterns " + matchingPatterns);
}
//获取最佳匹配
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean名称还是解析处理程序?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
//可能有多个“最佳模式”,让我们确保我们有正确的URI模板变量匹配他们
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
logger.trace("URI variables " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
再来看返回方法buildPathExposingHandler:
/ * *
*为给定的原始处理程序构建一个处理程序对象,公开实际的
*处理程序,{@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}以及
*执行处理程序之前的{@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE}。
*
默认实现构建一个{@link HandlerExecutionChain}
*使用一个特殊的拦截器来公开路径属性和uri模板变量
* @param rawHandler要公开的raw处理程序
* @param pathWithinMapping path to expose before执行处理程序
* @param uriTemplateVariables URI模板变量,如果没有找到变量,可以是{@code null}
* @return最终的处理程序对象
* /
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {
HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
}
return chain;
}
在buildPathExposingHandler方法中给Handler注册两个内部拦截器PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor,这两个拦截器分别在preHandle中调用了exposePathWithinMapping和exposeUriTemplateVariables方法将相应内容设置到了request的属性。
3.2 初始化
从spring容器或者子类加载handler。
/**
*调用{@link #detectHandlers()}方法
*超类的初始化。
*/
@Override
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
/**
*注册在当前ApplicationContext中找到的所有处理程序。
处理程序的实际URL确定取决于具体情况
* {@link #determineUrlsForHandler(String)}实现。一个豆
*没有这样的url可以被确定,就是不被认为是一个处理程序。
* @throws org.springframework.beans。如果处理程序不能注册,则BeansException异常
* @see # determineUrlsForHandler(字符串)
*/
protected void detectHandlers() throws BeansException {
/**
*为给定的URL路径注册指定的处理程序。
* @param urlPaths该bean应该映射到的url
处理程序bean的名称
* @抛出BeansException,如果处理程序不能被注册
* @抛出IllegalStateException,如果注册的处理程序有冲突
*/
protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
Assert.notNull(urlPaths, "URL path array must not be null");
for (String urlPath : urlPaths) {
registerHandler(urlPath, beanName);
}
}
注意:registerHandler方法并不是自己调用,也不是父类调用,而是子类调用。
/**
*为给定的URL路径注册指定的处理程序。
@param urlPath bean应该映射到的URL
处理程序实例或处理程序bean名称字符串
* (bean名称将自动解析为相应的处理程序bean)
* @抛出BeansException,如果处理程序不能被注册
* @抛出IllegalStateException,如果注册的处理程序有冲突
*/
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else {
if (urlPath.equals("/")) {
if (logger.isTraceEnabled()) {
logger.trace("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
else if (urlPath.equals("/*")) {
if (logger.isTraceEnabled()) {
logger.trace("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isTraceEnabled()) {
logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}
AbstractUrlHandlerMapping里面定义了整体架构,子类只需要将Map初始化就可以了。
4. SimpleUrlHandlerMapping
SimpleUrlHandlerMapping定义了一个Map变量(自己定义一个Map主要有两个作用,第一是方便配置,第二是可以在注册前做一些预处理,如确保所有url都以“/”开头),将所有的url和Handler的对应关系放在里面,最后注册到父类的Map中;而AbstractDetectingUrlHandlerMapping则是将容器中的所有bean都拿出来,按一定规则注册到父类的Map中。
/**
*调用{@link #registerHandlers}方法
*超类的初始化。
*/
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
registerHandlers内部又调用了Abstract-UrlHandlerMapping的registerHandler方法将我们配置的urlMap注册到AbstractUrlHandler-Mapping的Map中。
/**
*注册URL映射中指定的对应路径的所有处理程序。
@param urlMap以URL路径作为键,处理程序bean或bean名称作为值的映射
* @抛出BeansException,如果处理程序无法注册
* @抛出IllegalStateException,如果注册的处理程序有冲突
*/
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.trace("No patterns in " + formatMappingName());
}
else {
urlMap.forEach((url, handler) -> {
//如果还没有出现,则在前加上斜杠。
if (!url.startsWith("/")) {
url = "/" + url;
}
//从处理程序bean名称中删除空格。
if (handler instanceof String) {
handler = ((String) handler).trim();
}
//调用父类的负责方法
registerHandler(url, handler);
});
if (logger.isDebugEnabled()) {
List<String> patterns = new ArrayList<>();
if (getRootHandler() != null) {
patterns.add("/");
}
if (getDefaultHandler() != null) {
patterns.add("/**");
}
patterns.addAll(getHandlerMap().keySet());
logger.debug("Patterns " + patterns + " in " + formatMappingName());
}
}
}
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping:
5. AbstractDetectingUrlHandlerMapping
AbstractDetectingUrlHandlerMapping也是通过重写initApplicationContext来注册Handler的,里面调用了detectHandlers方法,在detectHandlers中根据配置的detectHand-lersInAn-cestorContexts参数从Spring MVC容器或者Spring MVC及其父容器中找到所有bean的beanName,然后用determineUrlsForHandler方法对每个beanName解析出对应的urls,如果解析结果不为空则将解析出的urls和beanName(作为Handler)注册到父类的Map,注册方法依然是调用AbstractUrlHandlerMapping的registerHandler方法。
使用beanName解析urls的determineUrlsForHandler方法是模板方法,交给具体子类实现。
/**
*调用{@link #detectHandlers()}方法
*超类的初始化。
*/
@Override
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
/**
*注册在当前ApplicationContext中找到的所有处理程序。
处理程序的实际URL确定取决于具体情况
* {@link #determineUrlsForHandler(String)}实现。一个豆
*没有这样的url可以被确定,就是不被认为是一个处理程序。
* @throws org.springframework.beans。如果处理程序不能注册,则BeansException异常
* @see # determineUrlsForHandler(字符串)
*/
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// 找到的URL路径:让我们把它看作一个处理程序。
//调用父类的registerHandler
registerHandler(urls, beanName);
}
}
if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
}
}
/ * *
*确定给定处理程序bean的url。
* @param beanName候选bean的名称
@返回为bean确定的url,如果没有,则返回空数组
* /
protected abstract String[] determineUrlsForHandler(String beanName);
目前它的子类只有:
BeanNameUrlHandlerMapping是检查beanName和alias是不是以“/”开头,如果是则将其作为url,里面只有一个determineUrlsForHandler方法:
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
/ * *
*检查给定bean的url名称和别名,以“/”开头。
* /
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<>();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = obtainApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
}
至此,关于HandlerMapping其实只要搞清楚,handler从哪里来,handler怎么调用,就差不多可以弄明白了。其他的基本上就是为了满足框架的可扩展性而设计使用的设计模式。
6. AbstractHandlerMethodMapping *
有难度建议多读。
AbstractHandlerMethodMapping直接继承自AbstractHandlerMapping。
AbstractHandlerMethodMapping系列是将Method作为Handler来使用的,这也是我们现在用得最多的一种Handler。
比如经常使用的@RequestMapping所注释的方法就是这种Handler,它专门有一个类型——HandlerMethod,也就是Method类型的Handler。
/ * *
*定义的{@link HandlerMapping}实现的抽象基类
请求和{@link HandlerMethod}之间的映射。
*
对于每个注册的处理程序方法,都维护一个惟一的映射
*定义映射类型{@code <T>}细节的子类。
*
* @param <T>包含条件的{@link HandlerMethod}的映射
*需要匹配一个传入请求的处理程序方法。
* /
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
6.1 初始化
先找到MappingRegistry这个内部类:
/ * *
*维护所有映射到处理程序方法,暴露方法的注册表
*执行查找和提供并发访问。
*
Package-private用于测试目的。
* /
class MappingRegistry {
这几个结构的内容初始化就是AbstractHandlerMethodMapping初始化要做的事,该实现了InitializingBean接口,所以spring容器会自动调用其afterPropertiesSet方法。
/ * *
*检测处理程序方法在初始化。
* @see # initHandlerMethods
* /
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
这里可能会疑惑,其父类不是也有一个初始化方法吗?大家可以思考一下。
其实很明显,是从容器中拿到这块handlermapping的。就是扫描容器加载完全后的操作。
initHandlerMethods进行HandlerMethod的注册操作,简单来说就是从springMVC的容器中获取所有的beanName,注册url和实现方法HandlerMethod的对应关系。
/ * *
*扫描ApplicationContext中的bean,检测和注册处理程序方法。
* @see # getCandidateBeanNames ()
* @see # processCandidateBean
* @see # handlerMethodsInitialized
* /
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
/ **
*限定作用域代理后面的目标Bean的Bean名称前缀。
用于从处理程序方法检测中排除那些
*目标,以支持相应的代理。
* 我们不检查autowire-candidate状态,这是
* *代理目标过滤的问题是如何被处理的自动装配水平,
*
* *自autowire-candidate可能已经转向其他
* * {@code假}的原因,同时还希望bean有资格的处理程序方法。
* * <p>最初在{@link org.springframework.aop.scope中定义。ScopedProxyUtils}
* *但是在这里复制是为了避免对spring-aop模块的严格依赖。*/
private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";
/ * *
*在检测到所有处理程序方法后调用。
带有处理程序方法和映射的只读映射。
* /
protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
// Total包括通过registerMapping检测到的映射+显式注册
int total = handlerMethods.size();
if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
logger.debug(total + " mappings in " + formatMappingName());
}
接着来看processCandidateBean:
/ * *
确定指定的候选bean和调用的类型
* {@link #detectHandlerMethods}如果标识为处理程序类型。
这个实现通过检查避免创建bean
* {@link org.springframework.beans.factory.BeanFactory #方法}
*并使用bean名调用{@link #detectHandlerMethods}。
* /
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
//一个无法解析的bean类型,可能来自一个懒惰的bean——我们忽略它。
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
根据beanName进行一系列的注册,最终实现是在registerHandlerMethod
/ * *
*在指定的处理程序bean中寻找处理程序方法。
一个bean名称或一个实际的处理程序实例
* @see # getMappingForMethod
* /
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
注册beanName和method及RequestMappingInfo之间的关系,RequestMappingInfo会保存url信息。
/ * *
*注册一个处理程序方法和它的唯一映射。在启动时调用,用于
*每个检测到的处理程序方法。
处理程序或处理程序实例的bean名称
* @param方法注册的方法
与处理程序方法相关联的映射条件
* @抛出IllegalStateException,如果已经注册了其他方法
*在相同的映射下
* /
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
这样我们就回到我们初开始看到的那几个类:
register方法如下:加入了并发条件控制,并将相应的信息加入到那几个map中去。
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping);
this.mappingLookup.put(mapping, handlerMethod);
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
6.2 使用(getHandlerInternal)
从org.springframework.web.servlet.DispatcherServlet#getHandler的入口开始:
来看看DispatcherServlet支持的HandlerMapping的类
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
先来查看改对象下的mappingRegistry:
基本上对应于上面看到的AbstractHandlerMethodMapping。接着往下走:
org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#getHandlerInternal。调用的其子类的getHandlerInternal
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}
随后才是org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
/ * *
*查找给定请求的处理程序方法。
* /
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//加了读写锁
this.mappingRegistry.acquireReadLock();
try {
//通过请求匹配出handler
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
/ * *
*查找当前请求的最佳匹配处理程序方法。
*如果找到多个匹配项,则选择最佳匹配项。
* @param lookupPath映射当前servlet映射中的查找路径
* @param请求当前请求
* @返回最佳匹配的处理程序方法,如果没有匹配则返回{@code null}
* @see #handleMatch(对象,字符串,HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
* /
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
//存储匹配上的结果
List<Match> matches = new ArrayList<>();
//基于url的mapping
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//扫描注册表mapping
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
//没有选择,只能通过所有映射…
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
//匹配到,获取最佳
if (!matches.isEmpty()) {
//将结果排序
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
//获取第一个最优解
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
//获取第二个最优解
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
//如果两个优先级相等,那么说明有问题。这两个方法冲突。
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
看看addMatchingMappings方法:
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
//遍历mapping
for (T mapping : mappings) {
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
}
}
}
这个mapping是
再回到:
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain
看看此时的handler:
已经找到了该链接下对应的方法,并且封装类型是HandlerMethod。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 对应上面handler 不是
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
找到对应的拦截器,返回最后的拦截器链。后面的工作就交给HandlerAdapter完成了。
其实可以来看一下他将HandlerMethod封装成HandlerExecutionChain的过程:
public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
if (handler instanceof HandlerExecutionChain) {
HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
this.handler = originalChain.getHandler();
this.interceptorList = new ArrayList<>();
CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
}
else {
this.handler = handler;
this.interceptors = interceptors;
}
}
总结:
HandlerMapping的整体结构在AbstractHandlerMapping中设计,简单来说其功能就是根据request找到Handler和Interceptors,组合成HandlerExecutionChain类型并返回。找Handler的过程通过模板方法getHandlerInternal留给子类实现,查找Interceptors则是AbstractHandlerMapping自己完成的,查找的具体方法是通过几个与Interceptor相关的Map完成的,在初始化过程中对那些Map进行了初始化。
难点:初始化,获取。
初始化基本上有spring的源码知识就可以很容易懂了。而且基本上只用看AbstractHandlerMethodMapping,因为用得比较多。