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是如何注册及查找处理器映射器,希望可帮助大家理解,有问题可继续交流。