SpringMvc源码分析-处理器映射器


  当一个请求发送到服务器后,springmvc是如何找到相应的处理器来处理呢?本篇博客将进行详细介绍

一、注册HandlerMapping

1.1、HandlerMapping初始化

  要先了解处理器映射器是如何注册到springmvc后,在分析查找流程就比较轻松了。springmvc是在DispatcherServlet中定义一个map保存HandlerMapping,具体如下:

/** List of HandlerMappings used by this servlet. */
@Nullable
private List<HandlerMapping> handlerMappings;

初始化handlerMappings,下面的注释很清晰

/**
 * Initialize the HandlerMappings used by this class.
 * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
 * we default to BeanNameUrlHandlerMapping.
 * 初始化 处理器映射器, 如果没有找不到则使用的默认的BeanNameUrlHandlerMapping
 */
private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	if (this.detectAllHandlerMappings) {//默认进入该分支
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
		// 在ioc容器中查找类型为HandlerMapping的bean
		/**
		 * 可以获取到bean满足以下条件之一即可:
		 * 1)使用注解方式
		 * 2)使用配置文件方式,并且配置了annotation-driven标签
		 */
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	else {
		try {
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerMapping later.
		}
	}

	// Ensure we have at least one HandlerMapping, by registering
	// a default HandlerMapping if no other mappings are found.
	/**
	 * 如果配置文件中没有配置<mvc:annotation-drivern/>则进入该if分支
	 * 默认情况下,在DispatcherServlet.properties文件中配置三个处理器映射器分别是
	 * BeanNameUrlHandlerMapping
	 * RequestMappingHandlerMapping
	 * RouterFunctionMapping
	 */
	if (this.handlerMappings == null) {//使用的默认的BeanNameUrlHandlerMapping
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
	}
	for (HandlerMapping mapping : this.handlerMappings) {
		if (mapping.usesPathPatterns()) {
			this.parseRequestPath = true;
			break;
		}
	}
}

1.2、注入HandlerMapping

  现在重点是springmvc是将哪些对象注入到spring ioc容器以及如何注入的?这里我们想一下spring管理的方式:xml方式和注解方式。那么springmvc当然也可以通过这两种方式注入

1.2.1、xml配置方式

  通过上面的代码分析,如果在配置文件中没有配置mvc:annotation-driven,就HandlerMapping不会有bean对象注入到ioc容器中,最后就会使用默认的HandlerMapping,具体处理在在方法getDefaultStrategies中,这里不再展开说明了。
  接下来我们着重介绍springmvc配置文件中配置mvc:annotation-driven的场景。springmvc处理xml解析都在这个包:org.springframework.web.servlet.config,如下图:在这里插入图片描述
这个标签解析处理是在AnnotationDrivenBeanDefinitionParser.java,具体解析方法parse,该方法内容太长,这里只把主要代码进行罗列:

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext context) {
	Object source = context.extractSource(element);
	XmlReaderContext readerContext = context.getReaderContext();

	CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
	context.pushContainingComponent(compDefinition);

	RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, context);

	// 创建处理器映射器,RequestMappingHandlerMapping
	RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
	handlerMappingDef.setSource(source);
	handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	handlerMappingDef.getPropertyValues().add("order", 0);
	handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);

	if (element.hasAttribute("enable-matrix-variables")) {
		boolean enableMatrixVariables = Boolean.parseBoolean(element.getAttribute("enable-matrix-variables"));
		handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
	}

	configurePathMatchingProperties(handlerMappingDef, element, context);
	readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);
    
    ...
    //创建处理器适配器,RequestMappingHandlerAdapter
	RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
	handlerAdapterDef.setSource(source);
	handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
	handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
	handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
	handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
	addRequestBodyAdvice(handlerAdapterDef);
	addResponseBodyAdvice(handlerAdapterDef);

	if (element.hasAttribute("ignore-default-model-on-redirect")) {
		Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
		handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
	}
	if (argumentResolvers != null) {
		handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
	}
	if (returnValueHandlers != null) {
		handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
	}
	if (asyncTimeout != null) {
		handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
	}
	if (asyncExecutor != null) {
		handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
	}

	handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
	handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
	readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);
	
	//创建异常相关解析器 ...

	context.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
	context.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
	context.registerComponent(new BeanComponentDefinition(uriContributorDef, uriContributorName));
	context.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));
	context.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));
	context.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));
	context.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));

	// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
	MvcNamespaceUtils.registerDefaultComponents(context, source);

	context.popAndRegisterContainingComponent();

	return null;
}

  因此我们可以得出:通过解析annotation-driven,springmvc会把相关处理器,适配器,异常解析器等注入spring ioc容器中

1.2.2、注解方式@EnableWebMvc

通过@EnableWebMvc注解定义可以知道,入口类是在DelegatingWebMvcConfiguration,通过简单阅读源码,可以很清晰知道在父类,是通过@Bean的方式注入到spring ioc容器中,具体代码:

@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(
		@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
		@Qualifier("mvcConversionService") FormattingConversionService conversionService,
		@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
	//创建RequestHandlerMapping
	RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
	mapping.setOrder(0);
	mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
	mapping.setContentNegotiationManager(contentNegotiationManager);
	mapping.setCorsConfigurations(getCorsConfigurations());

	PathMatchConfigurer pathConfig = getPathMatchConfigurer();
	if (pathConfig.getPatternParser() != null) {
		mapping.setPatternParser(pathConfig.getPatternParser());
	}
	else {
		mapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());
		mapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());

		Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
		if (useSuffixPatternMatch != null) {
			mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
		}
		Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
		if (useRegisteredSuffixPatternMatch != null) {
			mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
		}
	}
	Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
	if (useTrailingSlashMatch != null) {
		mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
	}
	if (pathConfig.getPathPrefixes() != null) {
		mapping.setPathPrefixes(pathConfig.getPathPrefixes());
	}
	return mapping;
}
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
		@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
		@Qualifier("mvcConversionService") FormattingConversionService conversionService,
		@Qualifier("mvcValidator") Validator validator) {
	//创建RequestMappingHandlerAdapter
	RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
	adapter.setContentNegotiationManager(contentNegotiationManager);
	adapter.setMessageConverters(getMessageConverters());
	adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
	adapter.setCustomArgumentResolvers(getArgumentResolvers());
	adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

	if (jackson2Present) {
		adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
		adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
	}

	AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();
	if (configurer.getTaskExecutor() != null) {
		adapter.setTaskExecutor(configurer.getTaskExecutor());
	}
	if (configurer.getTimeout() != null) {
		adapter.setAsyncRequestTimeout(configurer.getTimeout());
	}
	adapter.setCallableInterceptors(configurer.getCallableInterceptors());
	adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
	return adapter;
}

二、查找处理器映射器

2.1、业务自定义请求

查找处理器映射器的方法

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		for (HandlerMapping mapping : this.handlerMappings) {// 这里handlerMappings是在initHandlerMappings中赋值的
			HandlerExecutionChain handler = mapping.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

我们定义Controller通常使用@RequestMapping进行标注,最终匹配的处理器类是RequestMappingHandlerMapping,该类类图是:
在这里插入图片描述
匹配规则比较复杂,而且意义不是很大,简单说一下。我们在定义RequestMapping的时指定的value可以是:

@RequestMapping("/hello/wolrd")
@RequestMapping("/hello/${1}")
@RequestMapping("/hello/*")

需要字符串解析,匹配,case by case找到最优解,所以分析这个流程意义不大,过一段时间就又忘了。但是在这里可以说一下存储的数据结构MappingRegistry

class MappingRegistry {
	private final Map<T, MappingRegistration<T>> registry = new HashMap<>(); //T是mapping类型,比如RequestMappingInfo
	private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>(); //直接映射查找优先级最高, T是mapping类型,比如RequestMappingInfo
	private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();//按照名称注册
	private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>(); //跨域用的map
	private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	
	//其他内容忽略

	public void register(T mapping, Object handler, Method method) {
		this.readWriteLock.writeLock().lock();
		try {
			HandlerMethod handlerMethod = createHandlerMethod(handler, method);//包装类
			validateMethodMapping(handlerMethod, mapping);

			Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
			for (String path : directPaths) {
				this.pathLookup.add(path, mapping); //直接映射
			}

			String name = null;
			if (getNamingStrategy() != null) {//按照name进行存储映射
			    //例如控制器类为 MyHelloController, 处理方法是hello ==> name=MHC#hello
				name = getNamingStrategy().getName(handlerMethod, mapping);
				addMappingName(name, handlerMethod);
			}

			CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
			if (corsConfig != null) {
				corsConfig.validateAllowCredentials();
				this.corsLookup.put(handlerMethod, corsConfig); //跨域相关的map
			}
			//存储一个全局map,针对所有请求映射
			this.registry.put(mapping,
					new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
		}
		finally {
			this.readWriteLock.writeLock().unlock();
		}
	}
}

2.2、静态资源请求

静态资源处理需要在springmvc配置文件中开启默认servlet(这个servlet是由tomcat创建的)具体开启方式:

<mvc:default-servlet-handler/>

这个开启又做了哪些内容呢?

2.2.1、标签解析

可以参考这篇博客,SpringMvc源码分析–配置文件解析,里面注册了一个Servlet和一个处理器映射器,并且通过一个map将两者进行关联。当访问一个静态资源时,会找到这个处理器映射器,然后处理器映射器再返回这个servlet,用于真正处理这个请求。
在这里插入图片描述

三、总结

  这里介绍了springmvc是如何注册及查找处理器映射器,希望可帮助大家理解,有问题可继续交流。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值