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注册。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值