上一篇文章主要记录了springmvc父子容器初始化的整体脉络,许多细节问题都没有深入剖析。springmvc能够自动匹配请求url,查找到对应的类与方法进行处理,这一功能也是需要在初始化阶段进行一些准备的。这篇文章主要对映射关系的初始化做一些研究与记录。
(一)HandlerMapping
HandlerMapping的工作就是为每个请求找到一个合适的处理器handler,其实现机制简单来说就是维持了一个从url到请求处理器关系的Map结构。
HandlerMapping接口及实现类如下:
通过上图我们可以看到两个主要抽象类AbstractUrlHandlerMapping和AbstractHandlerMethodMapping。
- AbstractHandlerMethodMapping系列是将具体的Method作为Handler来使用的,也是我们用的最多的。比如经常使用的@RequestMapping所注释的方法就是这种Handler。
- AbstractUrlHandlerMapping是通过url来进行匹配的,大致原理是将url与对应的Handler保存在一个map中。
(二)RequestMappingHandlerMapping
在springmvc的配置文件中,这一行配置想必大家都不陌生。
<mvc:annotation-driven/>
springmvc在解析到这行配置时,会调用AnnotationDrivenBeanDefinitionParser的parse方法,其中就对RequestMappingHandlerMapping进行了注册。
RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping实现了InitializingBean接口,并重写了afterPropertiesSet()方法,进行了一些初始化操作,源码如下:
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
//获取springmvc上下文的所有注册的bean
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().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);
}
}
//isHandler由子类RequestMappingHandlerMapping重写,寻找符合拥有@Controller注解或是@RequestNMapping注解的类
if (beanType != null && isHandler(beanType)) {
//主要执行步骤,检测HandlerMethod形成映射关系
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
protected void detectHandlerMethods(final Object handler) {
Class<?> handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//寻找bean中方法的映射关系
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method 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);
}
//对查找到的映射关系进行注册,保存至内部类mappingRegistry对象中
for (Map.Entry<Method, T> entry : methods.entrySet()) {
registerHandlerMethod(handler, entry.getKey(), entry.getValue());
}
}
}
抽象方法getMappingForMethod由子类RequestMappingHandlerMapping实现,将Class上的@RequestMapping和Method上的@RequestMapping组装成RequestMappingInfo对象返回。源码如下:
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//解析方法上的@RequestMapping注解,封装为RequestMappingInfo对象
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
//解析类上的@RequestMapping注解,封装为RequestMappingInfo对象
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
//将类与方法的映射关系相组合
info = typeInfo.combine(info);
}
}
return info;
}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
//获取@RequestMapping注解信息
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class<?> ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
所以RequestMappingHandlerMapping保存的映射关系,实际上是键值对Map<RequestMappingInfo, HandlerMethod> mappingLookup。对于路径中不存在"?"和"*"的url,还会保存匹配关系到MultiValueMap<String, RequestMappingInfo> urlLookup中,其中key为方法的directUrl。
(三)SimpleUrlHandlerMapping
上一节讲的是AbstractHandlerMethodMapping实现类,接下来讲一下AbstractUrlHandlerMapping的实现类SimpleUrlHandlerMapping,通常用于静态资源映射。
在进行springmvc的配置时,通常我们会配置一个dispatcher servlet用于处理对应的URL。配置如下:
由于配置的拦截路径是"/",所有路径的请求都会转由DispatcherServlet处理。当试图访问静态资源路径时,由于没有对应的controller来处理请求,会导致访问失败。
需要在mvc配置文件中加入如下类似配置来解决静态资源映射问题。
<mvc:resources mapping="/assets/**" location="/assets/"/>
那这是怎么生效的呢?
mvc:resources标签对应的解析器是ResourcesBeanDefinitionParser。在parse方法中,解析器注册了SimpleUrlHandlerMapping,用于保存静态资源url与对应处理器的映射关系。SimpleUrlHandlerMapping的处理优先级是最低的,在其他HandlerMapping找不到映射关系时才会转由它处理。源码如下:
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
registerUrlProvider(parserContext, source);
//注册静态资源处理器ResourceHttpRequestHandler
String resourceHandlerName = registerResourceHandler(parserContext, element, source);
if (resourceHandlerName == null) {
return null;
}
Map<String, String> urlMap = new ManagedMap<String, String>();
String resourceRequestPath = element.getAttribute("mapping");
if (!StringUtils.hasText(resourceRequestPath)) {
parserContext.getReaderContext().error("The 'mapping' attribute is required.", parserContext.extractSource(element));
return null;
}
urlMap.put(resourceRequestPath, resourceHandlerName);
RuntimeBeanReference pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(null, parserContext, source);
RuntimeBeanReference pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(null, parserContext, source);
//注册SimpleUrlHandlerMapping
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//key为配置文件中的mapping,value为处理器ResourceHttpRequestHandler
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef).add("urlPathHelper", pathHelperRef);
String order = element.getAttribute("order");
//支持自定义mapping优先级,默认设置order为2147483646
handlerMappingDef.getPropertyValues().add("order", StringUtils.hasText(order) ? order : Ordered.LOWEST_PRECEDENCE - 1);
RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
String beanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
parserContext.getRegistry().registerBeanDefinition(beanName, handlerMappingDef);
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, beanName));
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
return null;
}
(四)总结
研读了springmvc中两个运用比较多的HandlerMapping的初始化过程,随后DispatcherServlet执行initHandlerMappings方法,直接从ApplicationContext容器中获取HandlerMapping列表,经由order排序后注入。
为后续文章分析DispatcherServlet的分发与处理请求过程打下基础。