SpringMVC之HandlerMapping
AbstractHandlerMapping是HandlerMapping的抽象實現,所有HandlerMapping都繼承自AbstractHandlerMapping。AbstractHandlerMapping採用模板模式設計了HandlerMapping實現的整體介面,它繼承了WebApplicationObjectSupport,初始化時會自動呼叫模板方法initApplicationContext:
protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.mappedInterceptors); initInterceptors(); }
extendInterceptors:用於給子類提供一個新增Interceptors的入口
detectMappedInterceptors:用於將SpringMVC容器以及父容器中所有MappedInterceptor型別的Bean新增到mappedInterceptors屬性:
protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( getApplicationContext(), MappedInterceptor.class, true, false).values()); }
initInterceptors:用來初始化Interceptor
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)); } } } }
具體內容就是將interceptors屬性裡面的物件分類新增到兩個集合中
Interceptors:用於配置SpringMVC的攔截器。Interceptors並不會直接使用,而是通過initInterceptors方法按型別分配到mappedInterceptors和adaptedInterceptors再使用
mappedInterceptors:此類Interceptor在使用時會與請求的url進行匹配
adaptedInterceptors:這種型別的Interceptor不需要進行匹配,在getHandler中會全部新增到返回值HandlerExecutionChain裡面
HandlerMapping通過getHandler方法獲取處理器Handler和攔截器Interceptor:
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);
}
首先會通過getHandlerInternal根據request獲取Handler,然後呼叫getHandlerExecutionChain返回一個HandlerExecutionChain:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//建立一個HandlerExecutionChain
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//新增所有的adaptedInterceptors
chain.addInterceptors(getAdaptedInterceptors());
//根據路徑匹配mappedInterceptor
String lookupPath = this.u rlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
在這個方法中就是先建立一個HandlerExecutionChain,然後將匹配的攔截器都新增進去
AbstractUrlHandlerMapping
它是將url與對應的Handler儲存在一個Map中,通過getHandlerInternal返回具體的Handler
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//獲取請求的url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
//根據url獲取Handler
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// 根據不同的情況獲取Handler
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);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
if (handler != null && logger.isDebugEnabled()) {
logger.debug("Mapping [" + lookupPath + "] to " + handler);
}
else if (handler == null && logger.isTraceEnabled()) {
logger.trace("No handler mapping found for [" + lookupPath + "]");
}
return handler;
}
下面看一下查詢的過程:
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 首先直接嘗試根據url獲取handler
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// 如果不為空,那麼封裝好後直接返回
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// 可能是萬用字元匹配的url
List<String> matchingPatterns = new ArrayList<String>();
//將所有匹配的url新增到集合中
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
}
//將獲取到的url排序,然後選擇第一個
String bestPatternMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
bestPatternMatch = matchingPatterns.get(0);
}
if (bestPatternMatch != null) {
//獲取對應的handler
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);
//可能有多個排名第一個的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);
}
}
if (logger.isDebugEnabled()) {
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
在這個方法中有兩個主線,一個是直接查詢,找到了直接返回,另一個是通過萬用字元匹配,匹配出來可能有好多個, 需要對他們進行排序選在最優的那個:
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
String pathWithinMapping, 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;
}
在這個方法裡面註冊了兩個攔截器PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor。
下面看一下Map的初始化,初始化一共有兩個方法,一個是註冊url和Handler的名字,另一個就是註冊url和具體的Handler:
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;
// 如果沒有設定懶載入並且型別是String,直接從容器中獲取,並新增到Map中
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));
}
}
}
}
下面看一個它的子類SimpleUrlHandlerMapping,他重寫了initApplicationContext方法:
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
首先呼叫了父類的initApplicationContext方法,然後將自己Map裡面的Handler註冊到父類的Map中
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);
}
}
}
AbstractDetectingUrlHandlerMapping也是AbstractUrlHandlerMapping的子類,也是通過重寫initApplicationContext註冊Handler:
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
protected void detectHandlers() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
}
//獲取容器中所有的beanName
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
//匹配所有的beanName
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// 註冊到父類的Map中
registerHandler(urls, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
通過detectHandlers方法, 根據配置的detectHandlerInAncestorContexts引數從SpringMVC容器或者SpringMVC及其父容器中找到所有bean的beanName,然後用determineUrlsForHandler方法對每個beanName解析出對應的urls,如果解析結果不為空則將解析出的urls個beanName註冊到父類的Map。
AbstractHandlerMapping的另一個分支就是AbstractHandlerMethodMapping,它是將Method作為Handler來使用,就是我們用@RequestMapping註釋的方法
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
//獲取所有的beanName
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
isHandler(getApplicationContext().getType(beanName))){
//將符合條件的beanName進行處理
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
首先拿到容器中的所有bean,然後根據一定的規則篩選出Handler,最後儲存到Map裡面,篩選使用的方法是isHandler,就是判斷是否有Controller或者RequestMapping註釋
protected void detectHandlerMethods(final Object handler) {
//獲取當前Handler的class物件
Class<?> handlerType =
(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// 用於儲存方法和條件
final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//選擇所有符合過濾條件的method
Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
@Override
public boolean matches(Method method) {
//獲取method的匹配條件
T mapping = getMappingForMethod(method, userType);
if (mapping != null) {
mappings.put(method, mapping);
return true;
}
else {
return false;
}
}
});
//註冊到Map中
for (Method method : methods) {
registerHandlerMethod(handler, method, mappings.get(method));
}
}
在這個方法中做了兩件事:
1、從傳入的處理器中找到符合要求的方法
2、註冊
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
//獲取方法上的RequestMapping的註釋
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
//封裝方法的匹配條件
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
我們發現在驗證這個方法是否符合要求主要是根據是否有RequestMapping註釋
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
//封裝handler和當前method
HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
//獲取當前匹配條件對應的HandlerMethod
HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
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.");
}
//將新的HandlerMethod 新增到map中
this.handlerMethods.put(mapping, newHandlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
}
//獲取匹配條件集合
Set<String> patterns = getMappingPathPatterns(mapping);
for (String pattern : patterns) {
//新增到map中
if (!getPathMatcher().isPattern(pattern)) {
this.urlMap.add(pattern, mapping);
}
}
//將name和HandlerMethod 新增到Map中
if (this.namingStrategy != null) {
String name = this.namingStrategy.getName(newHandlerMethod, mapping);
updateNameMap(name, newHandlerMethod);
}
}
下面看一下是如何找到對應的處理器的:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//獲得url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
//從map中找到對應的HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
//如果找到了建立要給新的HandlerMethod返回
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
我們看這個方法裡面一共做了三件事:
1、獲取url
2、獲取對應的HandlerMethod
3、如果找到了建立一個新的HandlerMethod返回
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
//從Map中找到當前url對應的所有過濾條件
List<T> directPathMatches = this.urlMap.get(lookupPath);
//將條件新增到matches中
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
}
if (!matches.isEmpty()) {
//對所有匹配條件進行排序,選取排名最好的那個
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 {
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}