上一篇文章主要記錄了springmvc父子容器初始化的整體脈絡(luò),許多細(xì)節(jié)問題都沒有深入剖析。springmvc能夠自動(dòng)匹配請(qǐng)求url鹅很,查找到對(duì)應(yīng)的類與方法進(jìn)行處理集漾,這一功能也是需要在初始化階段進(jìn)行一些準(zhǔn)備的。這篇文章主要對(duì)映射關(guān)系的初始化做一些研究與記錄摔敛。
(一)HandlerMapping
HandlerMapping的工作就是為每個(gè)請(qǐng)求找到一個(gè)合適的處理器handler,其實(shí)現(xiàn)機(jī)制簡(jiǎn)單來說就是維持了一個(gè)從url到請(qǐng)求處理器關(guān)系的Map結(jié)構(gòu)。
HandlerMapping接口及實(shí)現(xiàn)類如下:
通過上圖我們可以看到兩個(gè)主要抽象類AbstractUrlHandlerMapping和AbstractHandlerMethodMapping悄蕾。
- AbstractHandlerMethodMapping系列是將具體的Method作為Handler來使用的,也是我們用的最多的础浮。比如經(jīng)常使用的@RequestMapping所注釋的方法就是這種Handler帆调。
- AbstractUrlHandlerMapping是通過url來進(jìn)行匹配的,大致原理是將url與對(duì)應(yīng)的Handler保存在一個(gè)map中豆同。
(二)RequestMappingHandlerMapping
在springmvc的配置文件中番刊,這一行配置想必大家都不陌生。
<mvc:annotation-driven/>
springmvc在解析到這行配置時(shí)诱告,會(huì)調(diào)用AnnotationDrivenBeanDefinitionParser的parse方法撵枢,其中就對(duì)RequestMappingHandlerMapping進(jìn)行了注冊(cè)民晒。
RequestMappingHandlerMapping的父類AbstractHandlerMethodMapping實(shí)現(xiàn)了InitializingBean接口,并重寫了afterPropertiesSet()方法锄禽,進(jìn)行了一些初始化操作潜必,源碼如下:
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上下文的所有注冊(cè)的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)) {
//主要執(zhí)行步驟沃但,檢測(cè)HandlerMethod形成映射關(guān)系
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中方法的映射關(guān)系
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
try {
//抽象方法磁滚,由子類實(shí)現(xiàn)具體邏輯
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);
}
//對(duì)查找到的映射關(guān)系進(jìn)行注冊(cè),保存至內(nèi)部類mappingRegistry對(duì)象中
for (Map.Entry<Method, T> entry : methods.entrySet()) {
registerHandlerMethod(handler, entry.getKey(), entry.getValue());
}
}
}
抽象方法getMappingForMethod由子類RequestMappingHandlerMapping實(shí)現(xiàn)宵晚,將Class上的@RequestMapping和Method上的@RequestMapping組裝成RequestMappingInfo對(duì)象返回垂攘。源碼如下:
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
//解析方法上的@RequestMapping注解,封裝為RequestMappingInfo對(duì)象
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
//解析類上的@RequestMapping注解淤刃,封裝為RequestMappingInfo對(duì)象
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
//將類與方法的映射關(guān)系相組合
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保存的映射關(guān)系晒他,實(shí)際上是鍵值對(duì)Map<RequestMappingInfo, HandlerMethod> mappingLookup。對(duì)于路徑中不存在"?"和"*"的url逸贾,還會(huì)保存匹配關(guān)系到MultiValueMap<String, RequestMappingInfo> urlLookup中陨仅,其中key為方法的directUrl。
(三)SimpleUrlHandlerMapping
上一節(jié)講的是AbstractHandlerMethodMapping實(shí)現(xiàn)類铝侵,接下來講一下AbstractUrlHandlerMapping的實(shí)現(xiàn)類SimpleUrlHandlerMapping灼伤,通常用于靜態(tài)資源映射。
在進(jìn)行springmvc的配置時(shí)咪鲜,通常我們會(huì)配置一個(gè)dispatcher servlet用于處理對(duì)應(yīng)的URL狐赡。配置如下:
由于配置的攔截路徑是"/",所有路徑的請(qǐng)求都會(huì)轉(zhuǎn)由DispatcherServlet處理疟丙。當(dāng)試圖訪問靜態(tài)資源路徑時(shí)颖侄,由于沒有對(duì)應(yīng)的controller來處理請(qǐng)求,會(huì)導(dǎo)致訪問失敗隆敢。
需要在mvc配置文件中加入如下類似配置來解決靜態(tài)資源映射問題发皿。
<mvc:resources mapping="/assets/**" location="/assets/"/>
那這是怎么生效的呢?
mvc:resources標(biāo)簽對(duì)應(yīng)的解析器是ResourcesBeanDefinitionParser拂蝎。在parse方法中穴墅,解析器注冊(cè)了SimpleUrlHandlerMapping,用于保存靜態(tài)資源url與對(duì)應(yīng)處理器的映射關(guān)系温自。SimpleUrlHandlerMapping的處理優(yōu)先級(jí)是最低的玄货,在其他HandlerMapping找不到映射關(guān)系時(shí)才會(huì)轉(zhuǎn)由它處理。源碼如下:
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
registerUrlProvider(parserContext, source);
//注冊(cè)靜態(tài)資源處理器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);
//注冊(cè)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優(yōu)先級(jí)松捉,默認(rèn)設(shè)置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;
}
(四)總結(jié)
研讀了springmvc中兩個(gè)運(yùn)用比較多的HandlerMapping的初始化過程,隨后DispatcherServlet執(zhí)行initHandlerMappings方法馆里,直接從ApplicationContext容器中獲取HandlerMapping列表隘世,經(jīng)由order排序后注入可柿。
為后續(xù)文章分析DispatcherServlet的分發(fā)與處理請(qǐng)求過程打下基礎(chǔ)。