深度理解SpringMVC中的HandlerMapping
HandlerMapping的初始化策略
HandlerMapping 的作用根據(jù) request 找到對(duì)應(yīng)的處理器 Handler ,在 HandlerMapping 接口中有一個(gè)唯一的方法 getHanler , HandlerMapping 接口的定義如下:
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}方法的實(shí)現(xiàn)非常靈活,只要能根據(jù) request 返回 HandlerExecutionChain 就可以了。
SpringMVC內(nèi)置了很多 HandlerMapping 的實(shí)現(xiàn)類,主體結(jié)構(gòu)如下圖所示:

從圖中可以看出, AbstractHandlerMapping 是 HandlerMapping 的最頂層抽象實(shí)現(xiàn),在 AbstractHandlerMapping 中定義了 HandlerMapping 的整體結(jié)構(gòu),子類只需要通過默認(rèn)方法提供初始值或具體的算法即可。
HandlerMapping 是SpringMVC處理請(qǐng)求過程中的一個(gè)重要組件,在SpringMVC啟動(dòng)時(shí)對(duì)容器中的 HandlerMapping 進(jìn)行初始化,初始化的位置在 DispatcherServlet 的 onRefresh() 方法中:
@Override
protected void onRefresh(ApplicationContext context) {
//初始化組件策略
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
//初始化HandlerMapping
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
//從Spring上下文中獲取HandlerMapping類型的Bean
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
OrderComparator.sort(this.handlerMappings);
}
}
else {
try {
//獲取名稱為handlerMapping的bean
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
//初始化默認(rèn)的HandlerMapping,即BeanNameUrlHandlerMapping
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}從上面的代碼中可以很清除的看見SpringMVC對(duì)HandlerMapping的初始化過程:
1.首先,根據(jù)detectAllHandlerMappings判斷是否從容器中查找所有實(shí)現(xiàn)了HandlerMapping的bean。detectAllHandlerMappings默認(rèn)為true,可以通過DispatcherServlet的初始化參數(shù)來修改默認(rèn)值
<init-param> <param-name>detectAllHandlerMappings</param-name> <param-value>false</param-value> </init-param>
2.如果detectAllHandlerMappings為false,則從容器中查找明為handlerMapping的bean作為HandlerMapping的唯一實(shí)例。
3.如果以上兩步都沒有找到合適的handlerMapping,則會(huì)初始化默認(rèn)的BeanNameUrlHandlerMapping
以上,是SpringMVC對(duì)HandlerMapping的初始化策略,下面在看一下HandlerMapping自身的初始化過程。
HandlerMapping自身的初始化過程
如上面所說, AbstractHandlerMapping 定義了HandlerMapping的整體結(jié)構(gòu),所以,HandlerMapping的初始化過程也在 HandlerMapping中定義 , AbstractHandlerMapping 繼承了 WebApplicationObjectSupport , AbstractHandlerMapping 的創(chuàng)建過程就是在 initApplicationContext 中實(shí)現(xiàn)的。
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
detectMappedInterceptors(this.mappedInterceptors);
initInterceptors();
}在說明該方法前,先來看一下在 AbstractHandlerMapping 中定義的三個(gè)與Interceptor有關(guān)的變量:
private final List<Object> interceptors = new ArrayList<Object>(); private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>(); private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();
- interceptors:用于配置SpringMVC中的攔截器,Interceptors并不會(huì)直接使用,而是通過initInterceptors方法按類型分配到mappedInterceptors和adaptedInterceptors中使用,interceptors只用于配置。
- mappedInterceptors:此類interceptor在使用時(shí),需要與請(qǐng)求的url進(jìn)行匹配,只有匹配成功在會(huì)被添加到getHandler返回值的HandlerExecutionChain中。
- adaptedInterceptors:這類interceptors不需要進(jìn)行匹配,在getHandler中全部添加到返回值HandlerExecutionChain里面。
下面在看一下這三個(gè)方法:
protected void extendInterceptors(List<Object> interceptors) {
}extendInterceptors 方法用于給子類提供初始化interceptor的入口。
protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(), MappedInterceptor.class, true, false).values());
}detectMapedInterceptors 方法從容器中查找所有 Mapped Interceptor 類型的bean,并放入 mappedInterceptors 中。
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");
}
if (interceptor instanceof MappedInterceptor) {
this.mappedInterceptors.add((MappedInterceptor) interceptor);
}
else {
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
}initInterceptors 方法將 interceptors 里面所有包含的對(duì)象按照類型添加到 mappedInterceptors 或 adaptedInterceptors
總結(jié)一下:
HandlerMapping的初始化始于WebContextObjectSupport的initApplicationContext方法,這個(gè)方法的作用的初始化interceptor,interceptor主要分為兩種,一種的mappedInterceptors,另一種是adaptedInterceptors,其中mappedInterptors在應(yīng)用是要跟url進(jìn)行匹配,而adapterInterceptors會(huì)被直接應(yīng)用。
HandlerMapping 的入口方法是 getHandler() , 在AbstractHandlerMapping 中,對(duì)這個(gè)方法進(jìn)行了實(shí)現(xiàn),同時(shí),也是定義 getHandler() 的主體邏輯,下面看一下:
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
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 = getApplicationContext().getBean(handlerName);
}
return getHandlerExecutionChain(handler, request);
}在 getHandler() 中,做了兩件事,第一是根據(jù) request 獲取 heandler ,這是HandlerMapping的主要作用, 由getHandlerInternal() 完成
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
但該方法是抽象方法,由子類實(shí)現(xiàn)。 getHandler() 做的第二件事是將 Handler 和 Interceptor 組裝成 HandlerExecutionChain 。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
chain.addInterceptors(getAdaptedInterceptors());
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}在這個(gè)方法中,首先初始化一個(gè) HandlerExecutionChain ,然后將所有adaptedInterceptor添加到 HandlerExecutionChain ,最后,根據(jù)url從 mappedInterceptors 中過濾出需要的Interceptor并放入 HandlerExecutionChain 中。
總結(jié)一下:
應(yīng)用HandlerMapping的入口方法是getHandler,AbstractionHandlerMapping定義了getHandler的整體結(jié)構(gòu):1.獲取Handler,這一步交給子類完成2.組裝hander和interceptor為HandlerExecutionChain所有的adaptedInterceptor會(huì)被方法到HandlerExecutionChain,與url匹配的mappedInterceptor會(huì)被放入到HandlerExecutionChain中。
上面說的是 AbstractHandlerMapping 的初始化過程和 HandlerMapping 的使用入口的邏輯。
下面詳述一下 HandlerMapping 的一個(gè)大分支: AbstractUrlHandlerMapping 。
AbstractUrlHandlerMapping
AbstractUrlHandlerMapping 的大致原理是實(shí)現(xiàn)從url到 Handler 的映射,映射關(guān)系存在在一個(gè)map中
所以, AbstractHandlerMapping 的主要工作就是:
1.初始化這個(gè)存放映射關(guān)系的map
2.從map中找到對(duì)應(yīng)的 Handler 作為 getHandlerInternal() 的返回值。
先來看一下 AbstractUrlHandlerMapping 中對(duì) getHandlerInternal() 的定義:
private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//跟url從handlerMap中找到Handler
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
//設(shè)置兩個(gè)內(nèi)置攔截器
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}總結(jié)一個(gè):
AbstractUrlHandlerMapping的getHandlerInternal()主要做了兩件事:1.根據(jù)url找到Handler2.位Handler設(shè)置兩個(gè)內(nèi)置攔截器,作用是將當(dāng)前url匹配的pattern、匹配條件和url模板參數(shù)設(shè)置到request作用域中
在根據(jù)url獲取handler的過程中,其實(shí)并不是簡單是getByKey(url),在編寫 Controller 時(shí),通常我們會(huì)通過模式匹配或者路徑變量來映射url,此時(shí)就需要更復(fù)雜的匹配過程,來看一下 lookupHandler() 是如何實(shí)現(xiàn)的:
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 首先,直接根據(jù)url從handlerMap中獲取
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// 如果沒有獲取到,按照模式匹配來找
List<String> matchingPatterns = new ArrayList<String>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
}
String bestPatternMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
//匹配過程中,可以有多個(gè)Handler,那么通過排序后,取優(yōu)先級(jí)最高的一個(gè)
bestPatternMatch = matchingPatterns.get(0);
}
if (bestPatternMatch != null) {
handler = this.handlerMap.get(bestPatternMatch);
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
//此處處理匹配出多個(gè)且優(yōu)先級(jí)相同的handler的情況
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}總結(jié)一下:
在根據(jù)url獲取Handler的過程中
1.首先,直接根據(jù)url從handlerMap中獲取Handler
2.如果沒有找到,按照模式匹配來找到合適的Handler
3.最后,呼應(yīng)getHandlerInternal()方法,設(shè)置兩個(gè)內(nèi)置的Interceptor
lookupHandler()返回的實(shí)際是HandlerExecutionChain,這也就與AbstractHandlerMapping中g(shù)etExecutionChain的邏輯相呼應(yīng):
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? ? ? ? ? ? ? ? ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); **首先判斷getHandlerInternal()方法返回的是否是一個(gè)HandlerExecutionChain,如果不是則創(chuàng)建。** ? ? ?
對(duì)于 buildPathExposingHandler() 方法就是完成上面說的設(shè)置兩個(gè)內(nèi)置攔截器的功能,具體邏輯很簡單,就不說了。
在應(yīng)用上面的邏輯查找 Handler 之前,一個(gè)重要的工作是對(duì)handlerMap進(jìn)行初始化。handlerMap的初始化方法定義在 AbstractUrlHandlerMapping 中,單具體的調(diào)用是由子類完成的。兩個(gè)方法如下:
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);
}
}
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;
if (getApplicationContext().isSingleton(handlerName)) {
resolvedHandler = getApplicationContext().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.isInfoEnabled()) {
logger.info("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
else if (urlPath.equals("/*")) {
if (logger.isInfoEnabled()) {
logger.info("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isInfoEnabled()) {
logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}很簡單,思路就是第一個(gè)方法調(diào)用第二個(gè)方法,第一個(gè)方法是將多個(gè)url映射到一個(gè) Handler ,另外需要注意的是,在 registHandler 中,對(duì) / 和 /* 做了特殊處理,這兩個(gè)路徑會(huì)被映射到 rootHandler 上。
以上就是 AbstractUrlHandlerMapping的主體邏輯 ,總結(jié)一下:
AbstractHandlerMapping做了兩件事:
1.提供初始化handlerMap的入口-registerHander(),這也是與子類交互的接口。
2.根據(jù)url從handlerMap中查找對(duì)應(yīng)的Handler,此處需要注意的是AbstractUrlHandlerMapping對(duì)根路徑做了特殊處理-映射到了rooterHandler。
SimpleUrlHandlerMapping
SimpleUrlHandlerMapping 是 AbstractUrlHanderMapping 的一個(gè)子類,實(shí)現(xiàn)了從url到 Controller 的映射。
由上文可知, AbstractUrlHandlerMapping 與 SimpleUrlHandlerMapping 的交互接口是 registerHandler ,所以,在 SimpleUrlHandlerMapping 中定義了一個(gè)urlMap,這個(gè)map的作用是保存從url到 Controller 的配置信息。
private final Map<String, Object> urlMap = new HashMap<String, Object>();
然后,、 `SimpleUrlHandlerMapping 實(shí)現(xiàn)了 WebApplicationObjectSupport ,并覆寫了 initApplicationContext() 方法
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}可以看出,首先調(diào)用父類方法完成對(duì)Interceptor的處理,然后調(diào)用了 registerHandlers()
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
}
else {
for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
String url = entry.getKey();
Object handler = entry.getValue();
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
// Remove whitespace from handler bean name.
if (handler instanceof String) {
handler = ((String) handler).trim();
}
registerHandler(url, handler);
}
}
}從上面的代碼可以看出, SimpleUrlHandlerMapping 的 registerHandlers 方法主要是對(duì)配置進(jìn)來的數(shù)據(jù)進(jìn)行簡單處理,然后調(diào)用了父類的 registerHandler 方法。
AbstractHandlerMethodMapping
AbstractHandlerMethodMapping 是與 AbstractUrlHandlerMapping 平行了另一種 HandlerMapping ,將方法作為處理器 Handler ,SpringMVC用 HandlerMethod 來表示這個(gè)類型的 Handler 。
從 HandlerMapping 的結(jié)構(gòu)圖中可以看出,此類一共包含三個(gè)類: AbstractHandlerMethodMapping , RequestMappingInfoHandlerMapping 和 RequestMappingHandlerMapping ,這三個(gè)類依次繼承。
AbstractHandlerMethodMapping 繼承自 AbstractHandlerMapping ,hook方法是 getHandlerInternal() ,下面從 初始化和應(yīng)用兩個(gè)方面來看一下 AbstractHandlerMethodMapping 。
AbstractHandlerMethodMapping的初始化
在 AbstractHandlerMethodMapping 中有三個(gè)Map
private final Map<T, HandlerMethod> handlerMethods private final MultiValueMap<String, T> urlMap private final MultiValueMap<String, HandlerMethod> nameMap
在這三個(gè)map中包含了一個(gè)泛型T,可以簡單理解為查找 HandlerMethod 時(shí)的匹配條件,從子類 RequestMappingInfoHandlerMapping 的定義中就可以看出這個(gè)T的類型:
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
....
}對(duì)RequestMappingInfo和RequestCondition的說明放在后面。
下面看一下上面三個(gè)map的作用:
- urlMap:存放請(qǐng)求的url與匹配HandlerMethod的RequestCondition之間的映射
- nameMap:存放Controleller的BeanName和RequestCondition之間的映射
- handlerMethods:存放RequestCondition和HandlerMethod之間的映射
這三個(gè)map中 urlMap 和 nameMap 的類型是 MultiValueMap ,可以把多個(gè)value映射到一個(gè)key上,這個(gè)類型的數(shù)據(jù)結(jié)構(gòu)從類定義中就可以很清楚的看出來:
public interface MultiValueMap<K, V> extends Map<K, List<V>> {
...
}這三個(gè)map是 AbstractHandlerMethodMapping 最主要的容器,它的初始化主要就是對(duì)這個(gè)三個(gè)map中的內(nèi)容進(jìn)行初始化。
由于 AbstractHandlerMethodMapping 實(shí)現(xiàn)了 InitializingBean 接口,所以在啟動(dòng)springmvc時(shí)會(huì)自動(dòng)調(diào)用 afterPropertiesSet() 方法,初始化工作就在這個(gè)方法中完成的。
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
//從spring容器中獲取所有bean的beanname
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
//遍歷所有BeanName
for (String beanName : beanNames) {
//判斷Bean是否包含Controller或者RequestMapping注解
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
isHandler(getApplicationContext().getType(beanName))){
//將Bean初始化到上面的map
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}在 initHandlerMethods() 中主要做的是篩選出符合可以作為HandlerMethod條件的Bean,然后調(diào)用 detectHandlerMethods() 將這些bean初始化到map中。
對(duì)于 handlerMethodsInitialized() 方法,只是一個(gè)模板方法,且子類并沒有使用,所以這個(gè)方法也就沒有意義。
總結(jié)一下:
- 在AbstractHandlerMethodMapping中有三個(gè)map:urlMap,nameMap和handlerMethods,用來完成從url到RequestCondition,以及從RequestCondition到HandlerMethod的映射。
- AbstractHandlerMethodMapping實(shí)現(xiàn)了InitializingBean接口,啟動(dòng)容器時(shí),調(diào)用afterPropertiesSet()方法完成初始化,初始化從initHandlerMethods()方法開始,首先,獲取所有beanname,然后在根據(jù)
- beanname獲取bean的實(shí)例,并判斷是否符合作為HandlerMethod的條件,若符合則交給detectHandlerMethods()方法,完成最終的初始化。
再來看 detectHandlerMethods() 方法:
protected void detectHandlerMethods(final Object handler) {
//獲取handler類型,此處的HandlerType仍然是類的class對(duì)象
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//從clazz中找到符合條件的Method
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
//將符合條件的method和RequestMappingInfo添加到mappings, 在后面使用
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
});
//遍歷所有符合條件的Method,并利用handler/method/requestCondition進(jìn)行注冊(cè)。
for (Method method : methods) {
registerHandlerMethod(handler, method, mappings.get(method));
}
}從代碼中可以看出, detectHandlerMethods() 方法主要的邏輯就是找出符合條件的Method,然后將其根據(jù)handler和requestcondtion進(jìn)行注冊(cè)。
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
//根據(jù)handler和Method創(chuàng)建HandlerMethod
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
//根據(jù)requestcondition從容器中獲取之前的HandlerMethod
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
//如果原有的HandlerMethod和新的HandlerMethod不相等,則拋異常
if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
"' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" +
oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
}
//否則,在handlerMethods中建立新的映射關(guān)系
this.handlerMethods.put(mapping, newHandlerMethod);
//將匹配的url與RequestCondition初始化到urlMap中
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
//初始化nameMap
if (this.namingStrategy != null) {
String name = this.namingStrategy.getName(newHandlerMethod, mapping);
updateNameMap(name, newHandlerMethod);
}
}到此, AbstractHandlerMethodMapping 的初始化就完成了。
AbstractHandlerMethodMapping的應(yīng)用
入口方法依然是 getHandlerInternal()
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//獲取訪問路徑
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//根據(jù)訪問路徑獲取HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}getHandlerInternal() 中最重要的一步就是 lookupHandlerMethod()
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//根據(jù)url從urlMap中獲取RequestCondition
List<T> directPathMatches = this.urlMap.get(lookupPath);
if (directPathMatches != null) {
//如果獲取到,添加到matches
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
//如果不能直接獲取到,則把所有能匹配的RequestCondition天添加到matches中 addMatchingMappings(this.handlerMethods.keySet(), matches, request);
}
if (!matches.isEmpty()) {
//對(duì)matches進(jìn)行排序,然后獲取第一個(gè)作為bestMatch返回
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
}
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException(
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
m1 + ", " + m2 + "}");
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
//如果仍然沒有,則返回null
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}總結(jié)一下:
AbstractHandlerMethodMapping的應(yīng)用:
1.獲取請(qǐng)求路徑
2.根據(jù)請(qǐng)求路徑找到HandlerMethod
分別從handlerMethods和urlMap中查找
到此這篇關(guān)于深度理解SpringMVC中的HandlerMapping的文章就介紹到這了,更多相關(guān)SpringMVC的HandlerMapping內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)時(shí)間與字符串互相轉(zhuǎn)換詳解
這篇文章主要為大家詳細(xì)介紹了Java中實(shí)現(xiàn)時(shí)間與字符串互相轉(zhuǎn)換的相關(guān)方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04
Spring中使用事務(wù)嵌套時(shí)需要警惕的問題分享
最近項(xiàng)目上有一個(gè)使用事務(wù)相對(duì)復(fù)雜的業(yè)務(wù)場(chǎng)景報(bào)錯(cuò)了。在絕大多數(shù)情況下,都是風(fēng)平浪靜,沒有問題。其實(shí)內(nèi)在暗流涌動(dòng),在有些異常情況下就會(huì)報(bào)錯(cuò),這種偶然性的問題很有可能就會(huì)在暴露到生產(chǎn)上造成事故,那究竟是怎么回事呢?本文就來簡單講講2023-04-04
通過實(shí)例了解JavaBean開發(fā)及使用過程解析
這篇文章主要介紹了通過實(shí)例了解JavaBean開發(fā)及使用過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
基于Spring Boot的Logback日志輪轉(zhuǎn)配置詳解
本篇文章主要介紹了基于Spring Boot的Logback日志輪轉(zhuǎn)配置詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-10-10
簡單了解springboot中的配置文件相關(guān)知識(shí)
這篇文章主要介紹了簡單了解springboot中的配置文件相關(guān)知識(shí),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
IntelliJ IDEA像Eclipse一樣打開多個(gè)項(xiàng)目的圖文教程
這篇文章主要介紹了IntelliJ IDEA像Eclipse一樣打開多個(gè)項(xiàng)目的方法圖文教程講解,需要的朋友可以參考下2018-03-03

