spring mvc源码解读二 ---BeanNameUrlHandlerMapping初始化

大家都知道,springmvc有两个非常重要的类,分别是BeanNameUrlHandlerMapping ,RequestMappingHandlerMapping 这两个类,这两个类是用来存放url对应的controller,如果一个请求过来,会去这两个类中找到对应的controller然后反射调用方法,当然如果是springboot就自己实现3个HandlerMapping去处理更多的请求,这里先说这两个,那么这两个类是哪里配置的呢,是在spring-webmvc resources/org/springframework/web/servlet 目录下的 DispatcherServlet.properties 文件中

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

好了,现在开始调试,首先调试BeanNameUrlHandlerMapping,这个类是用来初始化上篇文章的第二,第三种方式。先上一个堆栈图:

从图中可以看到,先调用HttpServletBean(是DispatherServlet的父类)的init方法,为什么调用init方法,因为我们在上文代码中设置registration.setLoadOnStartup(1); 其他的调用链不看,我们直接去到DispatherServlet的initStrategies,直接到initHandlerMappings(context),其他方法暂时不看,里面会找到默认的两个headleMapping,在DispatherServlet的getDefaultStrategies循环去创建bean,首先创建BeanNameUrlHandlerMapping,createBean这些操作先不看,因为这些是spring创建bean的流程,以后会写一些关于spring bean创建流程的文章加深记忆,关键是要看堆栈中initializeBean,里面应用了后置处理器去初始化BeanNameUrlHandlerMapping,这个后置处理器是ApplicationContextAwareProcessor,这里贴一下这个处理器的关键代码


	private void invokeAwareInterfaces(Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof EnvironmentAware) {
				((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
			}
			if (bean instanceof EmbeddedValueResolverAware) {
				((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
			}
			if (bean instanceof ResourceLoaderAware) {
				((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
			}
			if (bean instanceof ApplicationEventPublisherAware) {
				((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
			}
			if (bean instanceof MessageSourceAware) {
				((MessageSourceAware) bean).setMessageSource(this.applicationContext);
			}
			if (bean instanceof ApplicationContextAware) {
				((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
			}
		}
	}

由于BeanNameUrlHandlerMapping实现了ApplicationContextAware,所以走最后一个if,然后调用setApplicationContext,里面会调用initServletContext(this.servletContext);,最终看调用链会调AbstractDetectingUrlHandlerMapping的initApplicationContext方法,这个类是BeanNameUrlHandlerMapping的父类,然后调用detectHandlers方法,这里类有点多,主要还是要看回堆栈的调用链,就在这个方法处理了带有"/"开头的beanname都会放进BeanNameUrlHandlerMapping的handleMap里面,关键代码

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是调用BeanNameUrlHandlerMapping的实现,

public 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<>();
		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);
	}

}

从以上的代码看,就是beanName以"/"开头的都会记录对应关系。然后看一些调试信息,

最终,筛选出来的是带有斜杠开头的beanName,把程序往下跑

 BeanNameUrlHandlerMapping 里面的handlerMap已经有对应关系了。当有请求过来,就可以根据key来拿到对应的bean来做调用,总结来说,BeanNameUrlHandlerMapping就是把beanName是以“/”开头的,就会记录beanName与bean的关系,存到一个map中。下一篇会讲RequestMappingHandlerMapping的初始化,也就是我们最常用的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值