DispatcherServlet与HandlerMapping关系
客户端发送请求,web容器接受请求,如果请求与DispatcherServlet的请求映射路径(url-pattern)匹配,web容器将请求交给DispatcherServlet处理。DispatcherServlet在加载的过程中会对HandlerMapping进行初始化,(具体可参照SpringMVC源码中DispatherServlet类的initHandlerMappings(ApplicationContextcontext)方法)。DispatcherServlet初始化完成后会自动扫描applicationContext.xml中的bean,根据名称(也就是bean的id或者类型来查找,如果找到则使用这个bean,找不到则使用DispatcherServlet.properties中的默认组件.
handlerMapping初始化
简单来说就是用来存储所有URL与处理类的map关系,以及在请求过来的时候根据输入URL
匹配到对应的处理类
第一步:
DefaultAnnotationHandlerMapping继承了AbstractDetectingUrlHandlerMapping,
在AbstractDetectingUrlHandlerMapping中遍历所有的DispatcherServlet配置文件中的
所有bean,找到相应的URL并进行缓存
protected void detectHandlers() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: "
+ getApplicationContext());
}
//获取了所有的bean,值得注意的是此处仅取出所有DispatcherServlet相关配置
//文件涉及的所有类
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(),
Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// 遍历
for (String beanName : beanNames) {
//拿到Controller 上配置的所有URL
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// 将所有的 url与controller对应关系存储下来
registerHandler(urls, beanName);
}else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
第二步:
看一下这个类:DefaultAnnotationHandlerMapping,其中
determineUrlsForHandler(String beanName)和
determineUrlsForHandlerMethods(Class<?> handlerType,
final boolean hasTypeLevelMapping)
解析bean的@RequestMapping,
protected String[] determineUrlsForHandler(String beanName) {
ApplicationContext context = getApplicationContext();
Class<?> handlerType = context.getType(beanName);
//找到对应bean 类级别标注的所有的 @RequestMapping
RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
if (mapping != null) {
this.cachedMappings.put(handlerType, mapping);
Set<String> urls = new LinkedHashSet<String>();
String[] typeLevelPatterns = mapping.value();
if (typeLevelPatterns.length > 0) {
// 找到对应bean 方法级别的所有 @RequestMapping
String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
for (String typeLevelPattern : typeLevelPatterns) {
if (!typeLevelPattern.startsWith("/")) {
typeLevelPattern = "/" + typeLevelPattern;
}
boolean hasEmptyMethodLevelMappings = false;
for (String methodLevelPattern : methodLevelPatterns) {
if (methodLevelPattern == null) {
hasEmptyMethodLevelMappings = true;
}else {
String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
//将类级别@RequestMapping和方法级别的@RequestMapping组合放入list
addUrlsForPath(urls, combinedPattern);
}
}
if (hasEmptyMethodLevelMappings ||
org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
addUrlsForPath(urls, typeLevelPattern);
}
}
return StringUtils.toStringArray(urls);
}else {
// actual paths specified by @RequestMapping at method level
return determineUrlsForHandlerMethods(handlerType, false);
}
}
else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
// @RequestMapping to be introspected at method level
return determineUrlsForHandlerMethods(handlerType, false);
}else {
return null;
}
}
第三步:
AbstractUrlHandlerMapping.registerHandler(String urlPath, Object handler)
将url 和 controller放入缓存,后续处理url请求时将会用到。
protected void registerHandler(String urlPath, Object handler)
throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String)handler;
if (this.getApplicationContext().isSingleton(handlerName)) {
resolvedHandler = this.getApplicationContext().getBean(handlerName);
}
}
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException("Cannot map "
this.getHandlerDescription(handler) +
" to URL path [" + urlPath + "]: There is already " +
this.getHandlerDescription(mappedHandler) + " mapped.");
}
} else if (urlPath.equals("/")) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Root mapping to " + this.getHandlerDescription(handler));
}
this.setRootHandler(resolvedHandler);
} else if (urlPath.equals("/*")) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Default mapping to " + this.getHandlerDescription(handler));
}
this.setDefaultHandler(resolvedHandler);
} else {
this.handlerMap.put(urlPath, resolvedHandler);
if (this.logger.isInfoEnabled()) {
this.logger.info("Mapped URL path [" + urlPath + "] onto " +
this.getHandlerDescription(handler));
}
}
}