AbstractHandlerMapping
AbstractHandlerMapping这个类,在父类进行设置容器的时候,就会被调用到的。那么调用的是
1
2
3
4
5
6
7
8
9
10
11
12/** * Initializes the interceptors. * @see #extendInterceptors(java.util.List) * @see #initInterceptors() */ @Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.mappedInterceptors); initInterceptors(); }
1
2
3
4
5
6
7
8
9
10
11
12
13/** * Detect beans of type {@link MappedInterceptor} and add them to the list of mapped interceptors. * <p>This is called in addition to any {@link MappedInterceptor}s that may have been provided * via {@link #setInterceptors}, by default adding all beans of type {@link MappedInterceptor} * from the current context and its ancestors. Subclasses can override and refine this policy. * @param mappedInterceptors an empty list to add {@link MappedInterceptor} instances to */ protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( getApplicationContext(), MappedInterceptor.class, true, false).values()); }
initInterceptors主要是放了两种的拦截器进来,一种是mappedInterceptors另一种是adaptedInterceptors,然后mappedInterceptors类型的拦截器的话只能是匹配用户请求的url才能放到用户的请求的拦截链子中去,但是后面这个adaptedInterceptors的话就可以直接放进去的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22/** * Initialize the specified interceptors, checking for {@link MappedInterceptor}s and * adapting {@link HandlerInterceptor}s and {@link WebRequestInterceptor}s if necessary. * @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"); } if (interceptor instanceof MappedInterceptor) { this.mappedInterceptors.add((MappedInterceptor) interceptor); } else { this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } } }
真正使用的过程中一般是使用getHandler来获取拦截器和处理器的handler的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/** * Look up a handler for the given request, falling back to the default * handler if no specific one is found. * @param request current HTTP request * @return the corresponding handler instance, or the default handler * @see #getHandlerInternal */ @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); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43/** * Build a {@link HandlerExecutionChain} for the given handler, including * applicable interceptors. * <p>The default implementation builds a standard {@link HandlerExecutionChain} * with the given handler, the handler mapping's common interceptors, and any * {@link MappedInterceptor}s matching to the current request URL. Subclasses * may override this in order to extend/rearrange the list of interceptors. * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a * pre-built {@link HandlerExecutionChain}. This method should handle those * two cases explicitly, either building a new {@link HandlerExecutionChain} * or extending the existing chain. * <p>For simply adding an interceptor in a custom subclass, consider calling * {@code super.getHandlerExecutionChain(handler, request)} and invoking * {@link HandlerExecutionChain#addInterceptor} on the returned chain object. * @param handler the resolved handler instance (never {@code null}) * @param request current HTTP request * @return the HandlerExecutionChain (never {@code null}) * @see #getAdaptedInterceptors() */ protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); chain.addInterceptors(getAdaptedInterceptors()); // 这里将刚才设置的mappedInterceptors中的url进行匹配,就是上面提到的部分了。 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) { if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } return chain; }
// 这里将刚才设置的mappedInterceptors中的url进行匹配,就是上面提到的部分了。 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) { if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } return chain; }
AbstractURLHandlerMapping
1public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
大致的原理是将url与handler保存在一个Map里面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37/** * Look up a handler for the URL path of the given request. * @param request current HTTP request * @return the handler instance, or {@code null} if none found */ @Override protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); 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();// 如果是/的话就默认用了RootHandler了。 } if (rawHandler == null) { rawHandler = getDefaultHandler();// 如果是空的话就默认的handler。 } 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; }
LookUpHandler
如果能直接匹配到的话,就直接将匹配的返回了就好了。
但是呢,可能并没有匹配到,那就用正则匹配,然后来获取能够匹配到的handler,它会对其进行排序的,然后排序完之后,挑选第一个,但是,可能并不是只有一个的得分情况是第一个的那个值,也就是说可能存在多个url实际上和第一个的url的得分情况是一样的,那么就应该扫描后续的那些url部分,然后将她们放在了uriTemplateVariables里面。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69/** * Look up a handler instance for the given URL path. * <p>Supports direct matches, e.g. a registered "/test" matches "/test", * and various Ant-style pattern matches, e.g. a registered "/t*" matches * both "/test" and "/team". For details, see the AntPathMatcher class. * <p>Looks for the most exact pattern, where most exact is defined as * the longest path pattern. * @param urlPath URL the bean is mapped to * @param request current HTTP request (to expose the path within the mapping to) * @return the associated handler instance, or {@code null} if not found * @see #exposePathWithinMapping * @see org.springframework.util.AntPathMatcher */ protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // Direct match? 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); } // Pattern match? 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); if (logger.isDebugEnabled()) { logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns); } 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 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; }
buildPathExposingHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/** * Build a handler object for the given raw handler, exposing the actual * handler, the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}, as well as * the {@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE} before executing the handler. * <p>The default implementation builds a {@link HandlerExecutionChain} * with a special interceptor that exposes the path attribute and uri template variables * @param rawHandler the raw handler to expose * @param pathWithinMapping the path to expose before executing the handler * @param uriTemplateVariables the URI template variables, can be {@code null} if no variables found * @return the final handler object */ 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; }
这里的俩拦截器本身就是用于将当前的Url实际匹配的Pattern、匹配条件,和URL模版参数设置到Request请求中去。这样以后就可以从url中获取了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter { private final String bestMatchingPattern; private final String pathWithinMapping; public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) { this.bestMatchingPattern = bestMatchingPattern; this.pathWithinMapping = pathWithinMapping; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request); request.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings()); return true; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter { private final Map<String, String> uriTemplateVariables; public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) { this.uriTemplateVariables = uriTemplateVariables; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { exposeUriTemplateVariables(this.uriTemplateVariables, request); return true; } }
总结下:
从DespatcherServelet类中的getHandler开始进行调用,然后调用了AbstractHandlerMapping中的getHandler方法,然后里面有个getHandlerInternal方法,这个方法要求子类进行实现的,那么,在其子类中AbstractUrlHandlerMapping中,实现了这个方法,其中比较重要的是lookupHandler方法,然后这个方法会获取handlerMap中的url的映射对象,如果没有的话就说明直接获取不到,只能模式匹配通过url的匹配确定一个集合,然后排序,然后将排序结果选取第一个返回,但是也会有多个价值同等的情况,那么这些同等的元素将会全部封装进一个集合中去,然后这个结合连同刚才匹配到的第一个结果,通过封装进一个执行ExecutionChain中去,然后层层返回,在getHandler接收到返回值之后还要检查是不是string类型的,如果是的话就尝试获取该字符串对应的对象的实例,到这里确保了一个比较重要的概念,是handler到底是谁的问题,之前是各种字符串的handler其实是url的,但是为了映射成能够被调用的handler也就是controller等的类的实例,只能getBean的形式来从容器中获取类的实例了,最后将返回的结果封装称为执行链,但是这里需要注意的是getHandlerInternal返回的handler是经过处理以后的,有可能会已经封装为chain了,所以在最后封装成chain链的时候会进行一个确认,到底需要不需要自己进一步封装了。
里面这个handlerMap子类会自动的进行注册的。
AbstractUrlHandlerMapping
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64/** * Register the specified handler for the given URL paths. * @param urlPaths the URLs that the bean should be mapped to * @param beanName the name of the handler bean * @throws BeansException if the handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ 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); } } /** * Register the specified handler for the given URL path. * @param urlPath the URL the bean should be mapped to * @param handler the handler instance or handler bean name String * (a bean name will automatically be resolved into the corresponding handler bean) * @throws BeansException if the handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ 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);// 如果是斜杠就设置为roothandler } else if (urlPath.equals("/*")) { if (logger.isInfoEnabled()) { logger.info("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler);// 如果是/*就设置为defaultHandler } else { this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
上面的子类调用,注册使用的。
SimpleUrlHandlerMapping
1public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
调用了一个父类的initApplicationContext,然后调用registerHandlers方法,来完成注册handler,然后调用的就是父类的registerHandler方法了、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36/** * Calls the {@link #registerHandlers} method in addition to the * superclass's initialization. */ @Override public void initApplicationContext() throws BeansException { super.initApplicationContext(); registerHandlers(this.urlMap); } /** * Register all handlers specified in the URL map for the corresponding paths. * @param urlMap Map with URL paths as keys and handler beans or bean names as values * @throws BeansException if a handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54/** * Calls the {@link #detectHandlers()} method in addition to the * superclass's initialization. */ @Override public void initApplicationContext() throws ApplicationContextException { super.initApplicationContext(); detectHandlers(); } /** * Register all handlers found in the current ApplicationContext. * <p>The actual URL determination for a handler is up to the concrete * {@link #determineUrlsForHandler(String)} implementation. A bean for * which no such URLs could be determined is simply not considered a handler. * @throws org.springframework.beans.BeansException if the handler couldn't be registered * @see #determineUrlsForHandler(String) */ protected void detectHandlers() throws BeansException { if (logger.isDebugEnabled()) { logger.debug("Looking for URL mappings in application context: " + getApplicationContext()); }// 获取容器中的所有bean的名字 String[] beanNames = (this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); // 对每个beanName解析url,如果能解析到的话就注册到父类的map中。 // Take any bean name that we can determine URLs for. for (String beanName : beanNames) { String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) {// 如果能解析到就注册到父类 // URL paths found: Let's consider it a handler. registerHandler(urls, beanName); } else { if (logger.isDebugEnabled()) { logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); } } } }
// 对每个beanName解析url,如果能解析到的话就注册到父类的map中。 // Take any bean name that we can determine URLs for. for (String beanName : beanNames) { String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) {// 如果能解析到就注册到父类 // URL paths found: Let's consider it a handler. registerHandler(urls, beanName); } else { if (logger.isDebugEnabled()) { logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); } } } }
BeanNameUrlHandlerMapping
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping { /** * Checks name and aliases of the given bean for URLs, starting with "/". */ @Override protected String[] determineUrlsForHandler(String beanName) { List<String> urls = new ArrayList<String>(); if (beanName.startsWith("/")) { urls.add(beanName); } String[] aliases = getApplicationContext().getAliases(beanName); for (String alias : aliases) { if (alias.startsWith("/")) { urls.add(alias); } } return StringUtils.toStringArray(urls); } }
设置beanName和alias是不是以/开头,如果是的话就将其作为url.
AbstractControllerUrlHandlerMapping
1
2
3
4
5
6
7
8
9
10
11
12
13
14/** * This implementation delegates to {@link #buildUrlsForHandler}, * provided that {@link #isEligibleForMapping} returns {@code true}. */ @Override protected String[] determineUrlsForHandler(String beanName) { Class<?> beanClass = getApplicationContext().getType(beanName); if (isEligibleForMapping(beanName, beanClass)) { return buildUrlsForHandler(beanName, beanClass); } else { return null; } }
将实现了Controller或者是标记了Controller注解的类的bean作为handler,并且可以通过设置excludedClasses和excludedPackages将不包含的bean或者不包含的包下的所有bean排除在外。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27protected boolean isEligibleForMapping(String beanName, Class<?> beanClass) { if (beanClass == null) { if (logger.isDebugEnabled()) { logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " + "because its bean type could not be determined"); } return false; } if (this.excludedClasses.contains(beanClass)) { if (logger.isDebugEnabled()) { logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " + "because its bean class is explicitly excluded: " + beanClass.getName()); } return false; } String beanClassName = beanClass.getName(); for (String packageName : this.excludedPackages) { if (beanClassName.startsWith(packageName)) { if (logger.isDebugEnabled()) { logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " + "because its bean class is defined in an excluded package: " + beanClass.getName()); } return false; } } return isControllerType(beanClass);// 是不是controller类型 }
ControllerClassNameHandlerMapping和ControllerBeanNameHandlerMapping,一个是使用了beanName作为url一个是使用的className作为的url.
AbstractHandlerMethodMapping
因为他实现了一个InitializingBean所以会自动调用其
1
2
3
4
5
6
7
8/** * Detects handler methods at initialization. */ @Override public void afterPropertiesSet() { initHandlerMethods(); }
这个方法,然后调用initHandlerMethods方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } 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))){ detectHandlerMethods(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
detectHandlerMethods方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26protected void detectHandlerMethods(final Object handler) { 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); Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() { @Override public boolean matches(Method method) { T mapping = getMappingForMethod(method, userType); if (mapping != null) { mappings.put(method, mapping); return true; } else { return false; } } }); for (Method method : methods) {// 将符合要求的handler保存到三个map中去。 registerHandlerMethod(handler, method, mappings.get(method)); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51/** * Register a handler method and its unique mapping. * @param handler the bean name of the handler or the handler instance * @param method the method to register * @param mapping the mapping conditions associated with the handler method * @throws IllegalStateException if another method was already registered * under the same mapping */ protected void registerHandlerMethod(Object handler, Method method, T mapping) { HandlerMethod newHandlerMethod = createHandlerMethod(handler, method); 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 methodn" + oldHandlerMethod + " mapped."); } // 添加handlermethods this.handlerMethods.put(mapping, newHandlerMethod); if (logger.isInfoEnabled()) { logger.info("Mapped "" + mapping + "" onto " + newHandlerMethod); } Set<String> patterns = getMappingPathPatterns(mapping); for (String pattern : patterns) { if (!getPathMatcher().isPattern(pattern)) { this.urlMap.add(pattern, mapping);// 添加urlMap中 } } if (this.namingStrategy != null) { String name = this.namingStrategy.getName(newHandlerMethod, mapping); updateNameMap(name, newHandlerMethod);// 添加updateNameMap中、 } }
// 添加handlermethods this.handlerMethods.put(mapping, newHandlerMethod); if (logger.isInfoEnabled()) { logger.info("Mapped "" + mapping + "" onto " + newHandlerMethod); } Set<String> patterns = getMappingPathPatterns(mapping); for (String pattern : patterns) { if (!getPathMatcher().isPattern(pattern)) { this.urlMap.add(pattern, mapping);// 添加urlMap中 } } if (this.namingStrategy != null) { String name = this.namingStrategy.getName(newHandlerMethod, mapping); updateNameMap(name, newHandlerMethod);// 添加updateNameMap中、 } }
在AbstractHandlerMapping中实际上在调用getHandler的时候调用了一个getInternalHandler方法,
这个方法在AbstractHandlerMethodMapping中也进行了实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } 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 + "]"); } } return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); List<T> directPathMatches = this.urlMap.get(lookupPath); 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 {// 无法使用lookupPath得到匹配条件,将所有的匹配条件加入matches中去。 return handleNoMatch(handlerMethods.keySet(), lookupPath, request); } }
RequestMappingHandlerMapping
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23/** * Uses method and type-level @{@link RequestMapping} annotations to create * the RequestMappingInfo. * @return the created RequestMappingInfo, or {@code null} if the method * does not have a {@code @RequestMapping} annotation. * @see #getCustomMethodCondition(Method) * @see #getCustomTypeCondition(Class) */ @Override protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = null; 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来找方法的,如果没有注解的,就返回null了。
总结一下:
如何匹配的方法呢?
封装,将方法封装成一个RequestMappingInfo类,里面封装了多个的相关的方法标记中可能会有的参数。以次来标识一个方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> { private final String name; private final PatternsRequestCondition patternsCondition; private final RequestMethodsRequestCondition methodsCondition; private final ParamsRequestCondition paramsCondition; private final HeadersRequestCondition headersCondition; private final ConsumesRequestCondition consumesCondition; private final ProducesRequestCondition producesCondition; private final RequestConditionHolder customConditionHolder;
整个Method类型的HandlerMapping的话主要的流程如下:
首先是AbstractHandlerMethodMapping类实现了InitilizingBean接口,那么会现调用afterPropertiesSet来设置其参数,调用initMethodHandler方法,然后这个方法中会调用detectHandlerMethod方法,这个方法会将handler保存在Map中去,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26protected void detectHandlerMethods(final Object handler) { 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); Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() { @Override public boolean matches(Method method) { T mapping = getMappingForMethod(method, userType); if (mapping != null) { mappings.put(method, mapping);// 筛选出来能够进行匹配的方法添加到这个map中去。 return true; } else { return false; } } }); for (Method method : methods) { registerHandlerMethod(handler, method, mappings.get(method));// 进行相应的注册 } }
注册分为三个不同的map
一个handlerMap保存的是匹配条件和方法的映射关系。
一个urlMap保存的是url和方法的映射关系。
一个nameMap是保存的方法名和MethodHandler的对应关系
后面这俩属于多值的情况。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26protected void registerHandlerMethod(Object handler, Method method, T mapping) { HandlerMethod newHandlerMethod = createHandlerMethod(handler, method); 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 methodn" + oldHandlerMethod + " mapped."); } this.handlerMethods.put(mapping, newHandlerMethod); if (logger.isInfoEnabled()) { logger.info("Mapped "" + mapping + "" onto " + newHandlerMethod); } Set<String> patterns = getMappingPathPatterns(mapping); for (String pattern : patterns) { if (!getPathMatcher().isPattern(pattern)) { this.urlMap.add(pattern, mapping); } } if (this.namingStrategy != null) { String name = this.namingStrategy.getName(newHandlerMethod, mapping); updateNameMap(name, newHandlerMethod); } }
上面完成一个初始化的过程,然后调用过程中会调用其handlerInternal方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18@Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } 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 + "]"); } } return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); }
这个方法里面主要是lookupHandlerMethod方法了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List<Match> matches = new ArrayList<Match>(); List<T> directPathMatches = this.urlMap.get(lookupPath);// 根据url获取可以进行匹配的method if (directPathMatches != null) {// 将匹配的条件添加到matches中去 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); } }
再总之:
整个的流程基本上就是先初始化基本数据,首先在AbstractHandlerMapping中会默认初始化一些拦截器,然后也与部分请求的路径等进行匹配,一旦匹配成功可以直接添加上,不过也存在不用匹配直接添加的情况。
AbstractUrlHandlerMapping这个是针对Url进行匹配的,主要的目的是找到Controller的类,它是根据AbstractHandlerMapping中的方法,getHandler方法也是这种类的外界访问切入口来调用的,内部访问了getHandlerInternal方法,然后这个方法会调用lookuppath寻找能够进行匹配的url对应的controller,然后在匹配的过程中如果直接能匹配就返回,不能的话就采用模式匹配,然后匹配结果再排个序,然后获取第零个作为返回的。不过如果存在多个和第零个价值一样的也会将其封装进一个chain中返回,这个chain是作为多重情况返回的,返回的结果回到父类AbstractHandlerMapping的getHandler中去,然后封装刚刚返回的数据称为一个chain链。然后直接作为结果返回就好了。
需要注意的是,在获取url与controller等的实例之间的关系时,不使用父类去挨着遍历的,而是在子类中自动注册到父类中的。所以AbstractUrlHandlerMapping中会有一个handlerMap的集合装有所以的映射关系。
AbstractMethodHandlerMapping这个是针对method来进行匹配的,
初始化过程中是因为实现了initilizingBean接口,然后会调用afterPropertiesSet方法,这个方法调用了initHandlerMethods方法,在里面会获取到所有的bean对象,然后又通过循环的方式调用了detectHandlerMethods(beanName)方法,这个方法会对类中的放进行选取,然后将选取到的符合条件的方法用于注册三个map,其实会用到controller和method和mappings.get(method).获取到的这个集合通过循环遍历的方式进行注册,会注册到handlerMap和urlMap和nameMap中去,到这里注册完成后基本上完成了初始化的过程。
调用的过程中实际上是调用了getHandlerInternal方法的,这个方法中会调用lookupHandlerMethod方法来对方法进行筛选,能匹配到的都放到一个集合里面去,然后对其进行排序,选取排序后的第一个作为最好的,如果还有第二个和第一个是一样的话就说明冲突了,抛出异常,然后将最好的这个匹配结果封装成methodResolver返回去,翻回去也就是返回到了getHandler中去了,那么接下来将得到的method的handler进行封装,将其封装为ExecutionChain然后返回即可完成了。
最后
以上就是还单身金毛最近收集整理的关于SpringMVC源码解读--HandlerMapping代码解读AbstractHandlerMappingAbstractURLHandlerMappingAbstractUrlHandlerMappingSimpleUrlHandlerMappingAbstractDetectingUrlHandlerMappingBeanNameUrlHandlerMappingAbstractControllerUrlHandlerMappingAbstractHandlerMethodMapping的全部内容,更多相关SpringMVC源码解读--HandlerMapping代码解读AbstractHandlerMappingAbstractURLHandlerMappingAbstractUrlHandlerMappingSimpleUrlHandlerMappingAbstractDetectingUrlHandlerMappingBeanNameUrlHandlerMappingAbstractControllerUrlHandlerMappingAbstractHandlerMethodMapping内容请搜索靠谱客的其他文章。
发表评论 取消回复