springboot源碼系列-HandlerMapping(2)

     讀完第一篇文章 總覺得這一塊哪還有缺失。所以還是繼續把上一篇文章缺失部分補全。我們接着上一遍文章講的SimpleUrlHandlerMapping  接下來講一下他的孿生兄弟 BeanNameUrlHandlerMapping和他倆的堂兄弟RequestMappingHandlerMapping(我們經常使用的一個HandlerMapping)

1,BeanNameUrlHandlerMapping

      BeanNameUrlHandlerMapping處理器映射器會根據請求的url與springIoc容器中定義的處理器bean的name屬性進行匹配

    

	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<>();
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		String[] aliases = obtainApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}

 通過代碼我們可以清楚的發現 只有當處理器的名字以/開頭才能將該bean映射成 映射處理器。

我們進一步找到調用這個方法地方它位於抽象父類的detectHandlers()方法中。而detectHandlers方法會被SpringIoc容器刷新結束調用。

	protected void detectHandlers() throws BeansException {
		ApplicationContext applicationContext = obtainApplicationContext();
		String[] beanNames = (this.detectHandlersInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
				applicationContext.getBeanNamesForType(Object.class));

		// Take any bean name that we can determine URLs for.
		for (String beanName : beanNames) {
			String[] urls = determineUrlsForHandler(beanName);
			if (!ObjectUtils.isEmpty(urls)) {
				// URL paths found: Let's consider it a handler.
				registerHandler(urls, beanName);
			}
		}

		if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
			logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
		}
	}

determineUrlsForHandler 這個方法是一個抽象方法 由實現它的子類去實現 spring慣用的伎倆模板方法 該模式幾乎在spring源碼裏面隨處可見。因此 我們可以自己定義映射規則。

2,RequestMappingHandlerMapping

         這個處理映射器 是我們經常使用的一個映射器 主要是將url和方法映射。我們debug去查看一下 它如何將Controller裏面的method映射成 處理器的。

protected void initHandlerMethods() {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for request mappings in application context: " + getApplicationContext());
		}
		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
				obtainApplicationContext().getBeanNamesForType(Object.class));

		for (String beanName : beanNames) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				Class<?> beanType = null;
				try {
					beanType = obtainApplicationContext().getType(beanName);
				}
				catch (Throwable ex) {
					// An unresolvable bean type, probably from a lazy bean - let's ignore it.
					if (logger.isDebugEnabled()) {
						logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
					}
				}
				if (beanType != null && isHandler(beanType)) {
					detectHandlerMethods(beanName);
				}
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

上述代碼可以描述爲 從SpringIoc容器中拿出所有的實例 獲取當前實例的類型  如果該實例複合一定的規則 就將進行detectHandlerMethods(beanName);處理。具體什麼規則 主要由我們的子類RequestMappingHandlerMapping實現 。當然我們也可以自定義實現。我們進入子類的isHandler(beanType)方法。

	@Override
	protected boolean isHandler(Class<?> beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

如果這個實例上面有Controller註解 或者RequestMapping註解其中的一個 RequestMappingHandlerMapping就認爲是合法的可以進行下一步處理。我們再看父類的處理流程

	protected void detectHandlerMethods(final Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			final Class<?> userType = ClassUtils.getUserClass(handlerType);
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isDebugEnabled()) {
				logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

首先找出這個類的所有方法映射成Map<Method,RequestMappingInfo> 具體實現放在子類實現 有興趣可以看一下子類的getMappingForMethod 這個方法。然後循環向mappingRegistry註冊。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章