框架-springmvc源码分析(二)
参考:
http://www.cnblogs.com/leftthen/p/5207787.html
http://www.cnblogs.com/leftthen/p/5208169.html
http://www.cnblogs.com/leftthen/p/5208376.html
http://www.cnblogs.com/leftthen/p/5208404.html
HandlerMapping - SimpleUrlHandlerMapping初始化
摘要:
SimpleUrlHandlerMapping只是参与Handler的注册,请求映射时由AbstractUrlHandlerMapping搞定.
初始化时,通过setMappings(Properties mappings)或者setUrlMap(Map<String, ?> urlMap)设置映射关系,然后通过WebApplicationObjectSupport的initApplicationContext调用registerHandlers进行注册.
覆写initApplicationContext方法时,先调用父类实现,然后才调用registerHandlers进行注册.其中最终的注册registerHandler实现是由AbstractUrlHandlerMapping实现的.
父类AbstractHandlerMapping继承了WebApplicationObjectSupport,所以SimpleUrlHandlerMapping可以通过覆写initApplicationContext注册Handler.
注册Handler前,不忘靠AbstractHandlerMapping来初始化拦截器.
// SimpleUrlHandlerMapping
1 /**
2 * Calls the {@link #registerHandlers} method in addition to the
3 * superclass's initialization.
4 */
5 @Override
6 public void initApplicationContext() throws BeansException {
7 super.initApplicationContext();
8 registerHandlers(this.urlMap);
9 }
initApplicationContext主要是进行拦截器的初始化.
extendInterceptors是留给子类用的扩展接口,暂时没有使用
detectMappedInterceptors是通过BeanFactoryUtils扫描应用下的全部MappedInterceptor类
initInterceptors初始化特定的拦截器,检查MappedInterceptor,在需要时适配adaptor HandlerInterceptor
// AbstractHandlerMapping
1 /**
2 * Initializes the interceptors.
3 * @see #extendInterceptors(java.util.List)
4 * @see #initInterceptors()
5 */
6 @Override
7 protected void initApplicationContext() throws BeansException {
8 extendInterceptors(this.interceptors);
9 detectMappedInterceptors(this.mappedInterceptors);
10 initInterceptors();
11 }
// AbstractHandlerMapping
1 /**
2 * Extension hook that subclasses can override to register additional interceptors,
3 * given the configured interceptors (see {@link #setInterceptors}).
4 * <p>Will be invoked before {@link #initInterceptors()} adapts the specified
5 * interceptors into {@link HandlerInterceptor} instances.
6 * <p>The default implementation is empty.
7 * @param interceptors the configured interceptor List (never {@code null}),
8 * allowing to add further interceptors before as well as after the existing
9 * interceptors
10 */
11 protected void extendInterceptors(List<Object> interceptors) {
12 }
springmvc中经常使用BeanFactoryUtils扫描应用下的类来进行初始化.
// AbstractHandlerMapping
1 /**
2 * Detects beans of type {@link MappedInterceptor} and adds them to the list of mapped interceptors.
3 * This is done in addition to any {@link MappedInterceptor}s that may have been provided via
4 * {@link #setInterceptors(Object[])}. Subclasses can override this method to change that.
5 *
6 * @param mappedInterceptors an empty list to add MappedInterceptor types to
7 */
8 protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {
9 mappedInterceptors.addAll(
10 BeanFactoryUtils.beansOfTypeIncludingAncestors(
11 getApplicationContext(),MappedInterceptor.class, true, false).values());
12 }
// AbstractHandlerMapping
1 /**
2 * Initialize the specified interceptors, checking for {@link MappedInterceptor}s and adapting
3 * HandlerInterceptors where necessary.
4 * @see #setInterceptors
5 * @see #adaptInterceptor
6 */
7 protected void initInterceptors() {
8 if (!this.interceptors.isEmpty()) {
9 for (int i = 0; i < this.interceptors.size(); i++) {
10 Object interceptor = this.interceptors.get(i);
11 if (interceptor == null) {
12 throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
13 }
14 if (interceptor instanceof MappedInterceptor) {
15 mappedInterceptors.add((MappedInterceptor) interceptor);
16 }
17 else {
18 adaptedInterceptors.add(adaptInterceptor(interceptor));
19 }
20 }
21 }
22 }
适配HandlerInterceptor和WebRequestHandlerInterceptorAdapter(什么是WebRequestHandlerInterceptorAdapter,晚点再说吧,具体看到时候拦截器部分的分析)
// AbstractHandlerMapping
1 /**
2 * Adapt the given interceptor object to the HandlerInterceptor interface.
3 * <p>Supported interceptor types are HandlerInterceptor and WebRequestInterceptor.
4 * Each given WebRequestInterceptor will be wrapped in a WebRequestHandlerInterceptorAdapter.
5 * Can be overridden in subclasses.
6 * @param interceptor the specified interceptor object
7 * @return the interceptor wrapped as HandlerInterceptor
8 * @see org.springframework.web.servlet.HandlerInterceptor
9 * @see org.springframework.web.context.request.WebRequestInterceptor
10 * @see WebRequestHandlerInterceptorAdapter
11 */
12 protected HandlerInterceptor adaptInterceptor(Object interceptor) {
13 if (interceptor instanceof HandlerInterceptor) {
14 return (HandlerInterceptor) interceptor;
15 }
16 else if (interceptor instanceof WebRequestInterceptor) {
17 return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
18 }
19 else {
20 throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
21 }
22 }
这才到SimpleUrlHandlerMapping干活的地方,迭代urlMap调用AbstractUrlHandlerMapping的registerHandler进行注册
(保障url以"/"开头就不多说了)
// SimpleUrlHandlerMapping
1 /**
2 * Register all handlers specified in the URL map for the corresponding paths.
3 * @param urlMap Map with URL paths as keys and handler beans or bean names as values
4 * @throws BeansException if a handler couldn't be registered
5 * @throws IllegalStateException if there is a conflicting handler registered
6 */
7 protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
8 if (urlMap.isEmpty()) {
9 logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
10 }
11 else {
12 for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
13 String url = entry.getKey();
14 Object handler = entry.getValue();
15 // Prepend with slash if not already present.
16 if (!url.startsWith("/")) {
17 url = "/" + url;
18 }
19 // Remove whitespace from handler bean name.
20 if (handler instanceof String) {
21 handler = ((String) handler).trim();
22 }
23 registerHandler(url, handler);
24 }
25 }
26 }
// AbstractUrlHandlerMapping
1 /**
2 * Register the specified handler for the given URL path.
3 * @param urlPath the URL the bean should be mapped to
4 * @param handler the handler instance or handler bean name String
5 * (a bean name will automatically be resolved into the corresponding handler bean)
6 * @throws BeansException if the handler couldn't be registered
7 * @throws IllegalStateException if there is a conflicting handler registered
8 */
9 protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
10 Assert.notNull(urlPath, "URL path must not be null");
11 Assert.notNull(handler, "Handler object must not be null");
12 Object resolvedHandler = handler;
13
14 // Eagerly resolve handler if referencing singleton via name.不是单例同时不是懒加载
15 if (!this.lazyInitHandlers && handler instanceof String) {
16 String handlerName = (String) handler;
17 if (getApplicationContext().isSingleton(handlerName)) {
18 resolvedHandler = getApplicationContext().getBean(handlerName);
19 }
20 }
21
22 Object mappedHandler = this.handlerMap.get(urlPath);// 获取之前已经匹配的Handler
23 if (mappedHandler != null) {
24 if (mappedHandler != resolvedHandler) {// 如果新匹配得到的跟之前已解析到的handler不一致,则抛异常
25 throw new IllegalStateException(
26 "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
27 "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
28 }
29 }
30 else {
31 if (urlPath.equals("/")) {// 设置rootHandler
32 if (logger.isInfoEnabled()) {
33 logger.info("Root mapping to " + getHandlerDescription(handler));
34 }
35 setRootHandler(resolvedHandler);
36 }
37 else if (urlPath.equals("/*")) {// 设置默认的defaultHandler
38 if (logger.isInfoEnabled()) {
39 logger.info("Default mapping to " + getHandlerDescription(handler));
40 }
41 setDefaultHandler(resolvedHandler);
42 }
43 else {// 最后才是普通handler的设置
44 this.handlerMap.put(urlPath, resolvedHandler);
45 if (logger.isInfoEnabled()) {
46 logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
47 }
48 }
49 }
50 }
HandlerMapping - AbstractDetectingUrlHandlerMapping系列初始化
AbstractDetectingUrlHandlerMapping是通过扫描方式注册Handler,收到请求时由AbstractUrlHandlerMapping的getHandlerInternal进行分发.
共有5个子类,一个抽象类.
与SimpleUrlHandlerMapping类似,通过覆写initApplicationContext,然后调用detectHandlers进行初始化.
detectHandlers通过BeanFactoryUtils扫描应用下的Object,然后预留determineUrlsForHandler给子类根据Handler生成对应的url.
注册使用的registerHandler依然由AbstractUrlHandlerMapping提供.
// AbstractDetectingUrlHandlerMapping
1 /**
2 * Calls the {@link #detectHandlers()} method in addition to the
3 * superclass's initialization.
4 */
5 @Override
6 public void initApplicationContext() throws ApplicationContextException {
7 super.initApplicationContext();
8 detectHandlers();
9 }
这边一样是调用AbstractHandlerMapping的initApplicationContext初始化拦截器.
主角上场,detectHandlers,扫描Handlers
// AbstractDetectingUrlHandlerMapping
1 /**
2 * Register all handlers found in the current ApplicationContext.
3 * <p>The actual URL determination for a handler is up to the concrete
4 * {@link #determineUrlsForHandler(String)} implementation. A bean for
5 * which no such URLs could be determined is simply not considered a handler.
6 * @throws org.springframework.beans.BeansException if the handler couldn't be registered
7 * @see #determineUrlsForHandler(String)
8 */
9 protected void detectHandlers() throws BeansException {
10 if (logger.isDebugEnabled()) {
11 logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
12 }
13 String[] beanNames = (this.detectHandlersInAncestorContexts ?
14 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
15 getApplicationContext().getBeanNamesForType(Object.class));
16
17 // Take any bean name that we can determine URLs for.
18 for (String beanName : beanNames) {
19 String[] urls = determineUrlsForHandler(beanName);
20 if (!ObjectUtils.isEmpty(urls)) {
21 // URL paths found: Let's consider it a handler.
22 registerHandler(urls, beanName);
23 }
24 else {
25 if (logger.isDebugEnabled()) {
26 logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
27 }
28 }
29 }
30 }
这边预留的模板方法定义如下:
1 /**
2 * Determine the URLs for the given handler bean.
3 * @param beanName the name of the candidate bean
4 * @return the URLs determined for the bean,
5 * or {@code null} or an empty array if none
6 */
7 protected abstract String[] determineUrlsForHandler(String beanName);
我们再来看看模板方法在BeanNameUrlHandlerMapping和AbstractControllerUrlHandlerMapping中的实现吧.
BeanNameUrlHandlerMapping非常简单,就实现了determineUrlsForHandler.
其中的alias应该是应该就是通过beanName在配置文件中配置的.
// BeanNameUrlHandlerMapping
1 /**
2 * Checks name and aliases of the given bean for URLs, starting with "/".
3 */
4 @Override
5 protected String[] determineUrlsForHandler(String beanName) {
6 List<String> urls = new ArrayList<String>();
7 if (beanName.startsWith("/")) {
8 urls.add(beanName);
9 }
10 String[] aliases = getApplicationContext().getAliases(beanName);
11 for (String alias : aliases) {
12 if (alias.startsWith("/")) {
13 urls.add(alias);
14 }
15 }
16 return StringUtils.toStringArray(urls);
17 }
再来看看AbstractControllerUrlHandlerMapping中的实现
isEligibleForMapping判断controller是否被排除在外(通过包package排除或类class排除).
buildUrlsForHandler由子类实现具体的url生成规则
isControllerType判断是否Controller的子类
buildUrlsForHandler预留给子类生产url的模板方法.
// AbstractControllerUrlHandlerMapping
1 /**
2 * This implementation delegates to {@link #buildUrlsForHandler},
3 * provided that {@link #isEligibleForMapping} returns {@code true}.
4 */
5 @Override
6 protected String[] determineUrlsForHandler(String beanName) {
7 Class beanClass = getApplicationContext().getType(beanName);
8 if (isEligibleForMapping(beanName, beanClass)) {
9 return buildUrlsForHandler(beanName, beanClass);
10 }
11 else {
12 return null;
13 }
14 }
// AbstractControllerUrlHandlerMapping
1 /**判断controller是否被排除在外(通过包package排除或类class排除).
2 * Determine whether the specified controller is excluded from this mapping.
3 * @param beanName the name of the controller bean
4 * @param beanClass the concrete class of the controller bean
5 * @return whether the specified class is excluded
6 * @see #setExcludedPackages
7 * @see #setExcludedClasses
8 */
9 protected boolean isEligibleForMapping(String beanName, Class beanClass) {
10 if (beanClass == null) {
11 if (logger.isDebugEnabled()) {
12 logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
13 "because its bean type could not be determined");
14 }
15 return false;
16 }
17 if (this.excludedClasses.contains(beanClass)) {
18 if (logger.isDebugEnabled()) {
19 logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
20 "because its bean class is explicitly excluded: " + beanClass.getName());
21 }
22 return false;
23 }
24 String beanClassName = beanClass.getName();
25 for (String packageName : this.excludedPackages) {
26 if (beanClassName.startsWith(packageName)) {
27 if (logger.isDebugEnabled()) {
28 logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
29 "because its bean class is defined in an excluded package: " + beanClass.getName());
30 }
31 return false;
32 }
33 }
34 return isControllerType(beanClass);
35 }
// AbstractControllerUrlHandlerMapping
1 /**
2 * Determine whether the given bean class indicates a controller type
3 * that is supported by this mapping strategy.
4 * @param beanClass the class to introspect
5 */
6 protected boolean isControllerType(Class beanClass) {
7 return this.predicate.isControllerType(beanClass);
8 }
// ControllerTypePredicate
这边提供2个api,分别判断是Controller的子类还是MultiActionController的子类.
1 /**
2 * Internal helper class that identifies controller types.
3 *
4 * @author Juergen Hoeller
5 * @since 2.5.3
6 */
7 class ControllerTypePredicate {
8
9 public boolean isControllerType(Class beanClass) {
10 return Controller.class.isAssignableFrom(beanClass);
11 }
12
13 public boolean isMultiActionControllerType(Class beanClass) {
14 return MultiActionController.class.isAssignableFrom(beanClass);
15 }
16
17 }
预留生成url的模板方法
// AbstractControllerUrlHandlerMapping
1 /**
2 * Abstract template method to be implemented by subclasses.
3 * @param beanName the name of the bean
4 * @param beanClass the type of the bean
5 * @return the URLs determined for the bean
6 */
7 protected abstract String[] buildUrlsForHandler(String beanName, Class beanClass);
再来看看AbstractControllerUrlHandlerMapping的2个实现ControllerBeanNameUrlHandlerMapping和ControllerClassNameUrlHandlerMapping.
其实这两个,很简单,一个是根据beanName来生产url,一个是根据className来生产url.
// ControllerBeanNameUrlHandlerMapping
1 @Override
2 protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
3 List<String> urls = new ArrayList<String>();
4 urls.add(generatePathMapping(beanName));
5 String[] aliases = getApplicationContext().getAliases(beanName);// 也获取配置的别名
6 for (String alias : aliases) {
7 urls.add(generatePathMapping(alias));
8 }
9 return StringUtils.toStringArray(urls);
10 }
// ControllerBeanNameUrlHandlerMapping
1 /**对path添加前后缀,还有/
2 * Prepends a '/' if required and appends the URL suffix to the name.
3 */
4 protected String generatePathMapping(String beanName) {
5 String name = (beanName.startsWith("/") ? beanName : "/" + beanName);
6 StringBuilder path = new StringBuilder();
7 if (!name.startsWith(this.urlPrefix)) {
8 path.append(this.urlPrefix);
9 }
10 path.append(name);
11 if (!name.endsWith(this.urlSuffix)) {
12 path.append(this.urlSuffix);
13 }
14 return path.toString();
15 }
// ControllerClassNameUrlHandlerMapping
直接委托给generatePathMappings实现
1 @Override
2 protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
3 return generatePathMappings(beanClass);
4 }
// ControllerClassNameUrlHandlerMapping
通过buildPathPrefix获取path的前缀
通过ClassUtils获取className,如BookController(不带包名),同时使用cglib代理的问题一并解决
根据大小写是否敏感,转换className(默认caseSensitive = false;)
isMultiActionControllerType判断Controller是否MultiActionController的子类,就是controller是否包含多个handler
1 /**
2 * Generate the actual URL paths for the given controller class.
3 * <p>Subclasses may choose to customize the paths that are generated
4 * by overriding this method.
5 * @param beanClass the controller bean class to generate a mapping for
6 * @return the URL path mappings for the given controller
7 */
8 protected String[] generatePathMappings(Class beanClass) {
9 StringBuilder pathMapping = buildPathPrefix(beanClass);
10 String className = ClassUtils.getShortName(beanClass);
11 String path = (className.endsWith(CONTROLLER_SUFFIX) ?
12 className.substring(0, className.lastIndexOf(CONTROLLER_SUFFIX)) : className);
13 if (path.length() > 0) {
14 if (this.caseSensitive) {
15 pathMapping.append(path.substring(0, 1).toLowerCase()).append(path.substring(1));
16 }
17 else {
18 pathMapping.append(path.toLowerCase());
19 }
20 }
21 if (isMultiActionControllerType(beanClass)) {
22 return new String[] {pathMapping.toString(), pathMapping.toString() + "/*"};
23 }
24 else {
25 return new String[] {pathMapping.toString() + "*"};
26 }
27 }
// ControllerClassNameUrlHandlerMapping
1 /**
2 * Build a path prefix for the given controller bean class.
3 * @param beanClass the controller bean class to generate a mapping for
4 * @return the path prefix, potentially including subpackage names as path elements
5 */
6 private StringBuilder buildPathPrefix(Class beanClass) {
7 StringBuilder pathMapping = new StringBuilder();
8 if (this.pathPrefix != null) {
9 pathMapping.append(this.pathPrefix);
10 pathMapping.append("/");
11 }
12 else {
13 pathMapping.append("/");
14 }
15 if (this.basePackage != null) {
16 String packageName = ClassUtils.getPackageName(beanClass);
17 if (packageName.startsWith(this.basePackage)) {
18 String subPackage = packageName.substring(this.basePackage.length()).replace('.', '/');
19 pathMapping.append(this.caseSensitive ? subPackage : subPackage.toLowerCase());
20 pathMapping.append("/");
21 }
22 }
23 return pathMapping;
24 }
// AbstractControllerUrlHandlerMapping
predicate.isMultiActionControllerType具体实现看上面的ControllerTypePredicate
1 /**
2 * Determine whether the given bean class indicates a controller type
3 * that dispatches to multiple action methods.
4 * @param beanClass the class to introspect
5 */
6 protected boolean isMultiActionControllerType(Class beanClass) {
7 return this.predicate.isMultiActionControllerType(beanClass);
8 }
HandlerMapping - AbstractUrlHandlerMapping系列request分发
AbstractHandlerMapping实现HandlerMapping接口定的getHandler
1. 提供getHandlerInternal模板方法给子类实现
2. 如果没有获取Handler,则使用默认的defaultHandler
3. 如果handler是string类型,从context获取实例
4. 通过getHandlerExecutionChain封装handler,添加interceptor
// AbstractHandlerMapping
1 /**
2 * Look up a handler for the given request, falling back to the default
3 * handler if no specific one is found.
4 * @param request current HTTP request
5 * @return the corresponding handler instance, or the default handler
6 * @see #getHandlerInternal
7 */
8 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
9 Object handler = getHandlerInternal(request);
10 if (handler == null) {
11 handler = getDefaultHandler();
12 }
13 if (handler == null) {
14 return null;
15 }
16 // Bean name or resolved handler?
17 if (handler instanceof String) {
18 String handlerName = (String) handler;
19 handler = getApplicationContext().getBean(handlerName);
20 }
21 return getHandlerExecutionChain(handler, request);
22 }
// AbstractHandlerMapping
1 /**
2 * Build a HandlerExecutionChain for the given handler, including applicable interceptors.
3 * <p>The default implementation simply builds a standard HandlerExecutionChain with
4 * the given handler, the handler mapping's common interceptors, and any {@link MappedInterceptor}s
5 * matching to the current request URL. Subclasses may
6 * override this in order to extend/rearrange the list of interceptors.
7 * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a pre-built
8 * HandlerExecutionChain. This method should handle those two cases explicitly,
9 * either building a new HandlerExecutionChain or extending the existing chain.
10 * <p>For simply adding an interceptor, consider calling {@code super.getHandlerExecutionChain}
11 * and invoking {@link HandlerExecutionChain#addInterceptor} on the returned chain object.
12 * @param handler the resolved handler instance (never {@code null})
13 * @param request current HTTP request
14 * @return the HandlerExecutionChain (never {@code null})
15 * @see #getAdaptedInterceptors()
16 */
17 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
18 HandlerExecutionChain chain =
19 (handler instanceof HandlerExecutionChain) ?
20 (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
21
22 chain.addInterceptors(getAdaptedInterceptors());
23
24 String lookupPath = urlPathHelper.getLookupPathForRequest(request);
25 for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
26 if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
27 chain.addInterceptor(mappedInterceptor.getInterceptor());
28 }
29 }
30
31 return chain;
32 }
接下来看看AbstractUrlHandlerMapping实现的getHandlerInternal
// AbstractUrlHandlerMapping
1 /**
2 * Look up a handler for the URL path of the given request.
3 * @param request current HTTP request
4 * @return the handler instance, or {@code null} if none found
5 */
6 @Override
7 protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
8 // 根据request获取url
9 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
10 // 根据url查找handler
11 Object handler = lookupHandler(lookupPath, request);
12 if (handler == null) {
13 // 如果没有匹配到handler需要查找默认的,下面需要将PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE缓存到request
14 // We need to care for the default handler directly, since we need to
15 // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
16 Object rawHandler = null;
17 if ("/".equals(lookupPath)) {
18 rawHandler = getRootHandler();
19 }
20 if (rawHandler == null) {
21 rawHandler = getDefaultHandler();
22 }
23 if (rawHandler != null) {
24 // Bean name or resolved handler?
25 if (rawHandler instanceof String) {
26 String handlerName = (String) rawHandler;
27 rawHandler = getApplicationContext().getBean(handlerName);
28 }
29 // 预留的校验handler模板方法,没有使用
30 validateHandler(rawHandler, request);
31 // 添加expose属性到request的拦截器
32 handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
33 }
34 }
35 if (handler != null && logger.isDebugEnabled()) {
36 logger.debug("Mapping [" + lookupPath + "] to " + handler);
37 }
38 else if (handler == null && logger.isTraceEnabled()) {
39 logger.trace("No handler mapping found for [" + lookupPath + "]");
40 }
41 return handler;
42 }
// AbstractUrlHandlerMapping
1 /**
2 * Look up a handler instance for the given URL path.
3 * <p>Supports direct matches, e.g. a registered "/test" matches "/test",
4 * and various Ant-style pattern matches, e.g. a registered "/t*" matches
5 * both "/test" and "/team". For details, see the AntPathMatcher class.
6 * <p>Looks for the most exact pattern, where most exact is defined as
7 * the longest path pattern.
8 * @param urlPath URL the bean is mapped to
9 * @param request current HTTP request (to expose the path within the mapping to)
10 * @return the associated handler instance, or {@code null} if not found
11 * @see #exposePathWithinMapping
12 * @see org.springframework.util.AntPathMatcher
13 */
14 protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
15 // Direct match? 直接根据url进行查找handler
16 Object handler = this.handlerMap.get(urlPath);
17 if (handler != null) {
18 // Bean name or resolved handler?
19 if (handler instanceof String) {
20 String handlerName = (String) handler;
21 handler = getApplicationContext().getBean(handlerName);
22 }
23 validateHandler(handler, request);
24 return buildPathExposingHandler(handler, urlPath, urlPath, null);
25 }
26 // Pattern match? 通过表达式进行匹配具体通过AntPathMatcher实现,具体后面分析
27 List<String> matchingPatterns = new ArrayList<String>();
28 for (String registeredPattern : this.handlerMap.keySet()) {
29 if (getPathMatcher().match(registeredPattern, urlPath)) {
30 matchingPatterns.add(registeredPattern);
31 }
32 }
33 String bestPatternMatch = null;
34 Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
35 if (!matchingPatterns.isEmpty()) {
36 Collections.sort(matchingPatterns, patternComparator);
37 if (logger.isDebugEnabled()) {
38 logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
39 }
40 // order序号最小的优先级最高
41 bestPatternMatch = matchingPatterns.get(0);
42 }
43 if (bestPatternMatch != null) {
44 handler = this.handlerMap.get(bestPatternMatch);
45 // Bean name or resolved handler?
46 if (handler instanceof String) {
47 String handlerName = (String) handler;
48 handler = getApplicationContext().getBean(handlerName);
49 }
50 validateHandler(handler, request);
51 String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
52
53 // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
54 // for all of them
55 Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
56 for (String matchingPattern : matchingPatterns) {
57 if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
58 Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
59 Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
60 uriTemplateVariables.putAll(decodedVars);
61 }
62 }
63 if (logger.isDebugEnabled()) {
64 logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
65 }
66 return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
67 }
68 // No handler found...
69 return null;
70 }
设计用于校验Handler,实际什么都没做,包括子类.
1 /**
2 * Validate the given handler against the current request.
3 * <p>The default implementation is empty. Can be overridden in subclasses,
4 * for example to enforce specific preconditions expressed in URL mappings.
5 * @param handler the handler object to validate
6 * @param request current HTTP request
7 * @throws Exception if validation failed
8 */
9 protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {
10 }
封装handler为HandlerExecutionChain,并添加PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor拦截器.
1 /**
2 * Build a handler object for the given raw handler, exposing the actual
3 * handler, the {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}, as well as
4 * the {@link #URI_TEMPLATE_VARIABLES_ATTRIBUTE} before executing the handler.
5 * <p>The default implementation builds a {@link HandlerExecutionChain}
6 * with a special interceptor that exposes the path attribute and uri template variables
7 * @param rawHandler the raw handler to expose
8 * @param pathWithinMapping the path to expose before executing the handler
9 * @param uriTemplateVariables the URI template variables, can be {@code null} if no variables found
10 * @return the final handler object
11 */
12 protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
13 String pathWithinMapping, Map<String, String> uriTemplateVariables) {
14
15 HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
16 chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
17 if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
18 chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
19 }
20 return chain;
21 }
HandlerMapping - RequestMappingHandlerMapping初始化
RequestMappingHandlerMapping ,用于注解@Controller,@RequestMapping来定义controller.
1 @Controller
2 @RequestMapping(value = "books")
3 public class BookController {
4
5 @RequestMapping(value = "/{id}")
6 @ResponseBody
7 public String getBook(@PathVariable("id") String id) {
8 // ...
9 return id;
10 }
11 }
初始化时,3个类的大致分工如下:
AbstractHandlerMethodMapping定义整个算法流程;
RequestMappingInfoHandlerMapping提供匹配条件RequestMappingInfo的解析处理;
RequestMappingHandlerMapping根据@RequestMapping注解生成 RequestMappingInfo,同时提供isHandler实现
整个初始化工作由AbstractHandlerMethodMapping的initHandlerMethods主导.
1. 使用BeanFactoryUtils扫描应用下的Object或者直接从容器中获取Object
2. 迭代类,分别判断isHandler判断目标类是否Handler
2.1 RequestMappingHandlerMapping.isHandler根据@Controller或@RequestMapping注解判断(有任意一个)
3. 对handler解析出所有需要分发的方法detectHandlerMethods
3.1 获取原始的Class<?>
3.2 使用HandlerMethodSelector.selectMethods过滤具体handler method,预留getMappingForMethod模板方法给子类
RequestMappingHandlerMapping.getMappingForMethod根据类,方法上的RequestMapping注解生成匹配条件RequestMappingInfo
3.3 对过滤到的每个method进行注册registerHandlerMethod
a, 使用createHandlerMethod封装处理器为HandlerMethod
b, 判断之前是否已经匹配条件对应的处理器是否冲突(相同的匹配条件只能有一个对应的处理器)
c, 设置匹配条件到handler method的映射关系
d, 从匹配条件中解析出url,并注册到urlMap(url到匹配条件的映射),这边由RequestMappingInfoHandlerMapping.getMappingPathPatterns实现
4. 对HandlerMethod进行初始化handlerMethodsInitialized,其实现在什么都没做
在讲初始化之前,我们先来聊聊使用到的一些概念
1. 映射关系,url到匹配条件RequestMappingInfo,匹配条件到HandlerMethod
2. 特殊的MultiValueMap,特别在value是个List
3. 使用到注解@Controller,@RequestMapping
4. 封装处理器信息的HandlerMethod
5. 封装各类匹配条件的RequestMappingInfo(诸如pattern,http method,request parameter等)
6. RequestCondition记录匹配条件
1. 进行request分发前,需要在初始化时准备好映射关系,这边AbstractHandlerMethodMapping中有两个属性保存了映射关系
// AbstractHandlerMethodMapping
1 // 匹配条件到HandlerMethod的映射
2 private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
3 // url到匹配条件的映射
4 private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
2. 这边的MultiValueMap其实挺简单,就是map的值是个list
1 public interface MultiValueMap<K, V> extends Map<K, List<V>> {
2 // ...
3 }
3. 我们再来看看这边使用到的两个注解:
// @Controller
1 // org.springframework.stereotype.Controller
2 @Target({ElementType.TYPE})
3 @Retention(RetentionPolicy.RUNTIME)
4 @Documented
5 @Component
6 public @interface Controller {
7
8 /**
9 * The value may indicate a suggestion for a logical component name,
10 * to be turned into a Spring bean in case of an autodetected component.
11 * @return the suggested component name, if any
12 */
13 String value() default "";
14
15 }
// @RequestMapping
1 // org.springframework.web.bind.annotation.RequestMapping
2 @Target({ElementType.METHOD, ElementType.TYPE})
3 @Retention(RetentionPolicy.RUNTIME)
4 @Documented
5 @Mapping
6 public @interface RequestMapping {
7
8 /**
9 * url路径,如/myPath/*.do
10 */
11 String[] value() default {};
12
13 /**
14 * HTTP request methods 如:GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
15 */
16 RequestMethod[] method() default {};
17
18 /**
19 * requeset parameter 有3种匹配方式,是否包含某个参数,参数值相等,参数值不等于某个值,如myParam!=myValue
20 */
21 String[] params() default {};
22
23 /**
24 * request的header
25 */
26 String[] headers() default {};
27
28 /**
29 * request的content type
30 */
31 String[] consumes() default {};
32
33 /**
34 * 返回内容的content type
35 */
36 String[] produces() default {};
37
38 }
39 }
4. HandlerMethod封装了处理器相关的全部信息,如类Object,方法Method,BeanFactory,参数MethodParameter[],原始方法Method
// HandlerMethod
1 // org.springframework.web.method.HandlerMethod
2 private final Object bean;// 因为final不可修改,所以下面每次需要修改信息时,都需要new一个
3
4 private final Method method;
5
6 private final BeanFactory beanFactory;
7
8 private final MethodParameter[] parameters;
9
10 private final Method bridgedMethod;
5. 这边匹配条件的范型只有一个实现,RequestMappingInfo.匹配条件里记录的是RequestCondition子类,用于诸如pattern,http method,request parameter等
// RequestMappingInfo
1 // javax.servlet.http.HttpServletRequest.RequestMappingInfo
2 public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {
3
4 private final PatternsRequestCondition patternsCondition;
5
6 private final RequestMethodsRequestCondition methodsCondition;
7
8 private final ParamsRequestCondition paramsCondition;
9
10 private final HeadersRequestCondition headersCondition;
11
12 private final ConsumesRequestCondition consumesCondition;
13
14 private final ProducesRequestCondition producesCondition;
15
16 private final RequestConditionHolder customConditionHolder;
17 // ...
18
19 }
6. 最后再简单看看RequestCondition ,这边定义了3个方法
1 package org.springframework.web.servlet.mvc.condition;
2 public interface RequestCondition<T> {
3 /**
4 * 拼接条件
5 */
6 T combine(T other);
7
8 /**
9 * 查找匹配的条件,并返回
10 */
11 T getMatchingCondition(HttpServletRequest request);
12
13 /**
14 * 用于排序
15 */
16 int compareTo(T other, HttpServletRequest request);
17 }
看看继承体系吧,老套路,定义接口,然后模板方法实现主要逻辑,具体算法留给子类实现,还有正事要做,还是后期再细化吧.
正文
整个初始化工作由AbstractHandlerMethodMapping的initHandlerMethods主导.copy一段,省得回去比对看
1. 使用BeanFactoryUtils扫描应用下的Object或者直接从容器中获取Object
2. 迭代类,分别判断isHandler判断目标类是否Handler
2.1 RequestMappingHandlerMapping.isHandler根据@Controller或@RequestMapping注解判断(有任意一个)
3. 对handler解析出所有需要分发的方法detectHandlerMethods
3.1 获取原始的Class<?>
3.2 使用HandlerMethodSelector.selectMethods过滤具体handler method,预留getMappingForMethod模板方法给子类
RequestMappingHandlerMapping.getMappingForMethod根据类,方法上的RequestMapping注解生成匹配条件RequestMappingInfo
3.3 对过滤到的每个method进行注册registerHandlerMethod
a, 使用createHandlerMethod封装处理器为HandlerMethod
b, 判断之前是否已经匹配条件对应的处理器是否冲突(相同的匹配条件只能有一个对应的处理器)
c, 设置匹配条件到handler method的映射关系
d, 从匹配条件中解析出url,并注册到urlMap(url到匹配条件的映射),这边由RequestMappingInfoHandlerMapping.getMappingPathPatterns实现
4. 对HandlerMethod进行初始化handlerMethodsInitialized,其实现在什么都没做
// AbstractHandlerMethodMapping
1 /** 这个方法哪来的,具体看备注的InitializingBean
2 * Detects handler methods at initialization.
3 */
4 public void afterPropertiesSet() {
5 initHandlerMethods();
6 }
7
8 /**扫描ApplicationContext中的bean,然后筛选handler method 并注册
9 * Scan beans in the ApplicationContext, detect and register handler methods.
10 * @see #isHandler(Class)
11 * @see #getMappingForMethod(Method, Class)
12 * @see #handlerMethodsInitialized(Map)
13 */
14 protected void initHandlerMethods() {
15 if (logger.isDebugEnabled()) {
16 logger.debug("Looking for request mappings in application context: " + getApplicationContext());
17 }
18
19 String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
20 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
21 getApplicationContext().getBeanNamesForType(Object.class));
22
23 for (String beanName : beanNames) {
24 if (isHandler(getApplicationContext().getType(beanName))){
25 detectHandlerMethods(beanName);
26 }
27 }
28 handlerMethodsInitialized(getHandlerMethods());
29 }
预留给子类实现的判断handler,实际是由RequestMappingHandlerMapping实现
// AbstractHandlerMethodMapping
1 /**
2 * Whether the given type is a handler with handler methods.
3 * @param beanType the type of the bean being checked
4 * @return "true" if this a handler type, "false" otherwise.
5 */
6 protected abstract boolean isHandler(Class<?> beanType);
// RequestMappingHandlerMapping
这边判断的逻辑很简单,类上使用Controller或RequestMapping其中至少一个注解就可以.
1 /**
2 * {@inheritDoc}
3 * Expects a handler to have a type-level @{@link Controller} annotation.
4 */
5 @Override
6 protected boolean isHandler(Class<?> beanType) {
7 return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
8 (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
9 }
// AbstractHandlerMethodMapping
1 /**
2 * Look for handler methods in a handler.
3 * @param handler the bean name of a handler or a handler instance
4 */
5 protected void detectHandlerMethods(final Object handler) {
6 Class<?> handlerType = (handler instanceof String) ?
7 getApplicationContext().getType((String) handler) : handler.getClass();
8
9 final Class<?> userType = ClassUtils.getUserClass(handlerType);
10
11 Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
12 public boolean matches(Method method) {
13 return getMappingForMethod(method, userType) != null;
14 }
15 });
16
17 for (Method method : methods) {
18 T mapping = getMappingForMethod(method, userType);
19 registerHandlerMethod(handler, method, mapping);
20 }
21 }
// AbstractHandlerMethodMapping
这边具体的实现是由RequestMappingHandlerMapping实现,根据注解生产匹配关系,这边实现类是RequestMappingInfo,就是代码有点多,慢慢看
1 /**
2 * Provide the mapping for a handler method. A method for which no
3 * mapping can be provided is not a handler method.
4 * @param method the method to provide a mapping for
5 * @param handlerType the handler type, possibly a sub-type of the method's
6 * declaring class
7 * @return the mapping, or {@code null} if the method is not mapped
8 */
9 protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
// RequestMappingHandlerMapping
1 /**
2 * Uses method and type-level @{@link RequestMapping} annotations to create
3 * the RequestMappingInfo.
4 *
5 * @return the created RequestMappingInfo, or {@code null} if the method
6 * does not have a {@code @RequestMapping} annotation.
7 *
8 * @see #getCustomMethodCondition(Method)
9 * @see #getCustomTypeCondition(Class)
10 */
11 @Override
12 protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
13 RequestMappingInfo info = null;
14 // 读取方法上的RequestMapping注解信息
15 RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
16 if (methodAnnotation != null) {
17 // 读取自定义的条件,这边没有使用
18 RequestCondition<?> methodCondition = getCustomMethodCondition(method);
19 // 根据方法上的RequsetMapping注解和自定义条件,生成匹配条件.这边的匹配条件包括http method,request parameter,request header等
20 info = createRequestMappingInfo(methodAnnotation, methodCondition);
21 // 读取类上的RequestMapping注解信息
22 RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
23 if (typeAnnotation != null) {
24 RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
25 // 生成类上的匹配条件,并合并方法上的
26 info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
27 }
28 }
29 return info;
30 }
// RequestMappingHandlerMapping
1 /**
2 * Created a RequestMappingInfo from a RequestMapping annotation.
3 */
4 private RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {
5 String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());
6 return new RequestMappingInfo(
7 new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),
8 this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
9 new RequestMethodsRequestCondition(annotation.method()),
10 new ParamsRequestCondition(annotation.params()),
11 new HeadersRequestCondition(annotation.headers()),
12 new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
13 new ProducesRequestCondition(annotation.produces(), annotation.headers(), getContentNegotiationManager()),
14 customCondition);
15 }
16
17 /**
18 * Resolve placeholder values in the given array of patterns.
19 * @return a new array with updated patterns
20 */
21 protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) {
22 if (this.embeddedValueResolver == null) {
23 return patterns;
24 }
25 else {
26 String[] resolvedPatterns = new String[patterns.length];
27 for (int i=0; i < patterns.length; i++) {
28 resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]);
29 }
30 return resolvedPatterns;
31 }
32 }
// AbstractHandlerMethodMapping
1 /**
2 * Register a handler method and its unique mapping.
3 * @param handler the bean name of the handler or the handler instance
4 * @param method the method to register
5 * @param mapping the mapping conditions associated with the handler method
6 * @throws IllegalStateException if another method was already registered
7 * under the same mapping
8 */
9 protected void registerHandlerMethod(Object handler, Method method, T mapping) {
10 HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
11 HandlerMethod oldHandlerMethod = handlerMethods.get(mapping);
12 if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
13 throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean()
14 + "' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '"
15 + oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
16 }
17
18 this.handlerMethods.put(mapping, newHandlerMethod);// 匹配条件requestMappingInfo 到处理器HandlerMethod
19 if (logger.isInfoEnabled()) {
20 logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
21 }
22
23 Set<String> patterns = getMappingPathPatterns(mapping);
24 for (String pattern : patterns) {
25 if (!getPathMatcher().isPattern(pattern)) {
26 this.urlMap.add(pattern, mapping);// url到匹配条件RequestMappingInfo
27 }
28 }
29 }
// AbstractHandlerMethodMapping
1 /**
2 * Create the HandlerMethod instance.
3 * @param handler either a bean name or an actual handler instance
4 * @param method the target method
5 * @return the created HandlerMethod
6 */
7 protected HandlerMethod createHandlerMethod(Object handler, Method method) {
8 HandlerMethod handlerMethod;
9 if (handler instanceof String) {
10 String beanName = (String) handler;
11 handlerMethod = new HandlerMethod(beanName, getApplicationContext(), method);
12 }
13 else {
14 handlerMethod = new HandlerMethod(handler, method);
15 }
16 return handlerMethod;
17 }
// AbstractHandlerMethodMapping
1 /**
2 * Extract and return the URL paths contained in a mapping.
3 */
4 protected abstract Set<String> getMappingPathPatterns(T mapping);
RequestMappingInfoHandlerMapping会实现这个模板方法
// RequestMappingInfoHandlerMapping
1 /**
2 * Get the URL path patterns associated with this {@link RequestMappingInfo}.
3 */
4 @Override
5 protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
6 return info.getPatternsCondition().getPatterns();
7 }
备注:
1. 这边的afterPropertiesSet是因为实现了InitializingBean接口
// org.springframework.beans.factory.InitializingBean
1 /**
2 * Interface to be implemented by beans that need to react once all their
3 * properties have been set by a BeanFactory: for example, to perform custom
4 * initialization, or merely to check that all mandatory properties have been set.
5 *
6 * <p>An alternative to implementing InitializingBean is specifying a custom
7 * init-method, for example in an XML bean definition.
8 * For a list of all bean lifecycle methods, see the BeanFactory javadocs.
9 *
10 * @author Rod Johnson
11 * @see BeanNameAware
12 * @see BeanFactoryAware
13 * @see BeanFactory
14 * @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
15 * @see org.springframework.context.ApplicationContextAware
16 */
17 public interface InitializingBean {
18
19 /**
20 * Invoked by a BeanFactory after it has set all bean properties supplied
21 * (and satisfied BeanFactoryAware and ApplicationContextAware).
22 * <p>This method allows the bean instance to perform initialization only
23 * possible when all bean properties have been set and to throw an
24 * exception in the event of misconfiguration.
25 * @throws Exception in the event of misconfiguration (such
26 * as failure to set an essential property) or if initialization fails.
27 */
28 void afterPropertiesSet() throws Exception;
29
30 }
HandlerMapping - RequestMappingHandlerMapping请求分发
AbstractHandlerMethodMapping实现接口getHandlerInternal,定义查找流程
RequestMappingInfoHandlerMapping根据RequestMappingInfo,细化匹配条件,并在匹配不到情况下,顽强的使用RequestCondition一再尝试匹配
虽然 RequestMappingHandlerMapping是受益方,但在这边什么都没做(就是在初始化时,根据@Controller,@RequestMapping注解生成RequestMappingInfo;并根据这两个注解判断是否目标Handler 实现isHandler)
AbstractHandlerMethodMapping实现接口getHandlerInternal
1. 使用UrlPathHelper查找request对应的path
2. 查找path对应的HandlerMethod
2.1 从urlMap中直接等值匹配查找匹配条件RequestMappingInfo
2.2 如果等值查找到匹配条件,将其添加到match条件中
2.3 如果没有找到匹配条件,使用所有的handlerMethod的RequestMappingInfo进行匹配
2.4 对匹配到的Match进行排序,取出最高优先级的Match,并核对是否是唯一的最高优先级
2.5 对匹配到条件,没有匹配到条件的两种情况,分别进行封装
3. 封装HandlerMethod,确保bean中存的是实例
// AbstractHandlerMethodMapping
实现接口getHandlerInternal
1 package org.springframework.web.servlet.handler
2 // AbstractHandlerMethodMapping<T>
3 /**
4 * Look up a handler method for the given request.
5 */
6 @Override
7 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
8 // 就是request对应的url
9 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
10 // 查找到处理器,这边的处理器会封装成HandlerMethod
11 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
12 // 确保bean中存的是实例
13 return (handlerMethod != null) ? handlerMethod.createWithResolvedBean() : null;
14 }
// AbstractHandlerMethodMapping
package org.springframework.web.servlet.handler;
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
/**
* Look up the best-matching handler method for the current request.
* If multiple matches are found, the best match is selected.
* @param lookupPath mapping lookup path within the current servlet mapping
* @param request the current request
* @return the best-matching handler method, or {@code null} if no match
* @see #handleMatch(Object, String, HttpServletRequest)
* @see #handleNoMatch(Set, String, HttpServletRequest)
*/
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<Match>();
// 从urlMap中直接等值匹配查找匹配条件RequestMappingInfo
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
// 没有匹配的情况下,遍历handlerMethods的全部匹配条件进行查找
addMatchingMappings(this.handlerMethods.keySet(), matches, request);
} if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator); 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();
// 不能有相同的最优Match
throw new IllegalStateException(
"Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
m1 + ", " + m2 + "}");
}
}
// 就是往request域中缓存url中解析出来的参数,mediaType等,这边RequestMappingHandlerMapping也覆写了一下
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
// RequestMappingHandlerMapping
return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
}
}
}
// AbstractHandlerMethodMapping
查找具体符合条件的RequestCondition
1 package org.springframework.web.servlet.handler;
2 public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
3
4 private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
5 for (T mapping : mappings) {
6 T match = getMatchingMapping(mapping, request);
7 if (match != null) {
8 matches.add(new Match(match, handlerMethods.get(mapping)));
9 }
10 }
11 }
// AbstractHandlerMethodMapping
1 /**
2 * Check if a mapping matches the current request and return a (potentially
3 * new) mapping with conditions relevant to the current request.
4 * @param mapping the mapping to get a match for
5 * @param request the current HTTP servlet request
6 * @return the match, or {@code null} if the mapping doesn't match
7 */
8 protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
我们来看看RequestMappingInfoHandlerMapping中的实现,从RequestMappingInfo中查找符合的RequestCondition
// RequestMappingInfoHandlerMapping
1 /**
2 * Check if the given RequestMappingInfo matches the current request and
3 * return a (potentially new) instance with conditions that match the
4 * current request -- for example with a subset of URL patterns.
5 * @return an info in case of a match; or {@code null} otherwise.
6 */
7 @Override
8 protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
9 return info.getMatchingCondition(request);
10 }
// AbstractHandlerMethodMapping
1 /**
2 * Invoked when a matching mapping is found.
3 * @param mapping the matching mapping
4 * @param lookupPath mapping lookup path within the current servlet mapping
5 * @param request the current request
6 */
7 protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) {
8 request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);
9 }
RequestMappingInfoHandlerMapping中又对其进行了覆写,具体是干啥用的,等看了HandlerAdaptor再说吧
1 /**
2 * Expose URI template variables, matrix variables, and producible media types in the request.
3 * @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
4 * @see HandlerMapping#MATRIX_VARIABLES_ATTRIBUTE
5 * @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
6 */
7 @Override
8 protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
9 super.handleMatch(info, lookupPath, request);
10
11 Set<String> patterns = info.getPatternsCondition().getPatterns();
12 String bestPattern = patterns.isEmpty() ? lookupPath : patterns.iterator().next();
13 request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
14
15 Map<String, String> uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
16 Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
17 request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
18
19 if (isMatrixVariableContentAvailable()) {
20 request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, extractMatrixVariables(request, uriVariables));
21 }
22
23 if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
24 Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
25 request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
26 }
27 }
1 /**
2 * Invoked when no matching mapping is not found.
3 * @param mappings all registered mappings
4 * @param lookupPath mapping lookup path within the current servlet mapping
5 * @param request the current request
6 * @throws ServletException in case of errors
7 */
8 protected HandlerMethod handleNoMatch(Set<T> mappings, String lookupPath, HttpServletRequest request)
9 throws Exception {
10
11 return null;
12 }
RequestMappingInfoHandlerMapping,覆写,不死心,再匹配一次
// RequestMappingInfoHandlerMapping
1 /**
2 * Iterate all RequestMappingInfos once again, look if any match by URL at
3 * least and raise exceptions accordingly.
4 * @throws HttpRequestMethodNotSupportedException if there are matches by URL
5 * but not by HTTP method
6 * @throws HttpMediaTypeNotAcceptableException if there are matches by URL
7 * but not by consumable/producible media types
8 */
9 @Override
10 protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos,
11 String lookupPath, HttpServletRequest request) throws ServletException {
12
13 Set<String> allowedMethods = new LinkedHashSet<String>(4);
14
15 Set<RequestMappingInfo> patternMatches = new HashSet<RequestMappingInfo>();
16 Set<RequestMappingInfo> patternAndMethodMatches = new HashSet<RequestMappingInfo>();
17
18 for (RequestMappingInfo info : requestMappingInfos) {
19 if (info.getPatternsCondition().getMatchingCondition(request) != null) {
20 patternMatches.add(info);
21 if (info.getMethodsCondition().getMatchingCondition(request) != null) {
22 patternAndMethodMatches.add(info);
23 }
24 else {
25 for (RequestMethod method : info.getMethodsCondition().getMethods()) {
26 allowedMethods.add(method.name());
27 }
28 }
29 }
30 }
31
32 if (patternMatches.isEmpty()) {
33 return null;
34 }
35 else if (patternAndMethodMatches.isEmpty() && !allowedMethods.isEmpty()) {
36 throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods);
37 }
38
39 Set<MediaType> consumableMediaTypes;
40 Set<MediaType> producibleMediaTypes;
41 Set<String> paramConditions;
42
43 if (patternAndMethodMatches.isEmpty()) {
44 consumableMediaTypes = getConsumableMediaTypes(request, patternMatches);
45 producibleMediaTypes = getProducibleMediaTypes(request, patternMatches);
46 paramConditions = getRequestParams(request, patternMatches);
47 }
48 else {
49 consumableMediaTypes = getConsumableMediaTypes(request, patternAndMethodMatches);
50 producibleMediaTypes = getProducibleMediaTypes(request, patternAndMethodMatches);
51 paramConditions = getRequestParams(request, patternAndMethodMatches);
52 }
53
54 if (!consumableMediaTypes.isEmpty()) {
55 MediaType contentType = null;
56 if (StringUtils.hasLength(request.getContentType())) {
57 try {
58 contentType = MediaType.parseMediaType(request.getContentType());
59 }
60 catch (IllegalArgumentException ex) {
61 throw new HttpMediaTypeNotSupportedException(ex.getMessage());
62 }
63 }
64 throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(consumableMediaTypes));
65 }
66 else if (!producibleMediaTypes.isEmpty()) {
67 throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(producibleMediaTypes));
68 }
69 else if (!CollectionUtils.isEmpty(paramConditions)) {
70 String[] params = paramConditions.toArray(new String[paramConditions.size()]);
71 throw new UnsatisfiedServletRequestParameterException(params, request.getParameterMap());
72 }
73 else {
74 return null;
75 }
76 }