SpringMVC源码关于HandlerMapping
从HandlerMapping的注册与初始化问题开始讲
因为是初学,我们根据springMVC的配置文件层层往下
spring-web.xml的代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- 开启注解 -->
<!-- springmvc使用<mvc:annotation-driven> -->
<!-- 自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter, -->
<!-- 可用在springmvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。 -->
<mvc:annotation-driven />
<mvc:default-servlet-handler />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="suffix" value=".jsp" />
</bean>
<context:component-scan base-package="com.alipay.web" />
</beans>
在这个配置文件中有四个部分
1、<mvc:annotation-driven />
开启springMVC的注解模式
2、<mvc:default-servlet-handler />
REST风格的资源URL不希望带 .html 或 .do 等后缀,为了将静态资源的请求转由Web容器处理,使用这个方法。对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。也就是静态资源处理。
3、配置页面的前置路径和后缀
4、扫描项目中的web包
而HandlerMapping的注册则跟第一个部分<mvc:annotation-driven />相关
Spring是怎么解析<mvc:annotation-driven/>标签的?
首先,必须要有一个继承自“org.springframework.beans.factory.xml.NamespaceHandlerSupport”的类,在其init方法中,注册自己的解析器,注册mvc解析器的类为MvcNamespaceHandler。这个类源码如下:
public class MvcNamespaceHandler extends NamespaceHandlerSupport {
public MvcNamespaceHandler() {
}
public void init() {
this.registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
this.registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
this.registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
this.registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
this.registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
this.registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser());
this.registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser());
this.registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
this.registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser());
this.registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser());
this.registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser());
this.registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser());
this.registerBeanDefinitionParser("script-template-configurer", new ScriptTemplateConfigurerBeanDefinitionParser());
this.registerBeanDefinitionParser("cors", new CorsBeanDefinitionParser());
}
}
从初始化方法中可以看到,对于annotation-driven有个解析器AnnotationDrivenBeanDefinitionParser。而这个解析器必须实现org.springframework.beans.factory.xml.BeanDefinitionParser接口。
这个类一开始便设置了两个常量
public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();
public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();
这两个常量分别是获取HandlerMapping和HandlerAdapter的类名
解析器的主要代码如下:
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
//格式转换处理拦截类,比如时间、数字等
parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
主要目的是注册与mvc处理有关的相关beans以及默认的mvc组件
这边介绍一下HandlerMapping的
//生成RequestMappingHandlerMapping组件对象
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//优先级设置为最高
handlerMappingDef.getPropertyValues().add("order", 0);
//添加contentNegotiationManager属性,处理media type handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
//查看mvc:annotation-driven有无enable-matrix-variables/enableMatrixVariables,表示是否开启多变量映射比如/cars;a=1;b=1
//具体使用可查阅相关文档,默认removeSemicolonContent为false
if (element.hasAttribute("enable-matrix-variables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
else if (element.hasAttribute("enableMatrixVariables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
//配置路径匹配解析器等属性
configurePathMatchingProperties(handlerMappingDef, element, parserContext);
//将RequestMappingHandlerMapping注册为bean对象放置bean工厂中 readerContext.getRegistry().registerBeanDefinition( HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);
RequestMappingHandlerMapping主要是处理@Controller和@RequestMapping注解的,这个是<mvc:annotation-driven />标签默认的配置的,从代码可以看出来,这个标签同时配置了RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter 两个bean。
configurePathMatchingProperties配置路径匹配解析器用的,代码如下:
private void configurePathMatchingProperties(RootBeanDefinition handlerMappingDef, Element element, ParserContext parserContext) {
Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching");
if (pathMatchingElement != null) {
Object source = parserContext.extractSource(element);
Boolean useRegisteredSuffixPatternMatch;
if (pathMatchingElement.hasAttribute("suffix-pattern")) {
useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("suffix-pattern"));
handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useRegisteredSuffixPatternMatch);
}
if (pathMatchingElement.hasAttribute("trailing-slash")) {
useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("trailing-slash"));
handlerMappingDef.getPropertyValues().add("useTrailingSlashMatch", useRegisteredSuffixPatternMatch);
}
if (pathMatchingElement.hasAttribute("registered-suffixes-only")) {
useRegisteredSuffixPatternMatch = Boolean.valueOf(pathMatchingElement.getAttribute("registered-suffixes-only"));
handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch);
}
RuntimeBeanReference pathHelperRef = null;
if (pathMatchingElement.hasAttribute("path-helper")) {
pathHelperRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-helper"));
}
pathHelperRef = MvcNamespaceUtils.registerUrlPathHelper(pathHelperRef, parserContext, source);
handlerMappingDef.getPropertyValues().add("urlPathHelper", pathHelperRef);
RuntimeBeanReference pathMatcherRef = null;
if (pathMatchingElement.hasAttribute("path-matcher")) {
pathMatcherRef = new RuntimeBeanReference(pathMatchingElement.getAttribute("path-matcher"));
}
pathMatcherRef = MvcNamespaceUtils.registerPathMatcher(pathMatcherRef, parserContext, source);
handlerMappingDef.getPropertyValues().add("pathMatcher", pathMatcherRef);
}
}
看代码逻辑,主要是通过mvc:annotation-driven下的mvc:path-matching节点来配置
1、suffix-pattern-是否启用后缀匹配,默认为true,即对指定的url会再新增.,比如/user实际匹配/user.->/user匹配/user.html,/user.jsp
2、trailing-slash-是否启动尾部斜线匹配,默认为true,即对指定的url会新增/,比如/user也会匹配/user/
3、registered-suffixes-only-是否启用media type类型的匹配,即对指定的url新增.json/.xml等匹配,默认为false
4、path-helper-路径获取帮助类,默认为UrlPathHelper类
5、path-matcher-路径匹配解析器,默认为AntPathMather
此处完成了对于handlerMapping的注册以后,程序正常走,首先进入到DispatcherServlet进行初始化操作
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
其中的方法initHandlerMappings(context)的代码如下:
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(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.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
语句:detectAllHandlerMappings会判断是否默认添加所有的HandlerMappings,如果存在进行下一步,取出上文注册好的handlerMapping,如果不是,就从配置文件中搜索是否存在相关的bean进行初始化。当两种情况下都未能找到handlerMapping的时候,程序会通过getDefaultStrategies()方法给handlerMappings赋值一个默认的值,这个默认的值在DispatcherServlet.properties中,这个配置文件如下:
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
到此完成了对于handlerMapping的初始化工作。
总结
1、通过spring-web.xml配置文件写入<mvc:annotation-driven />标签对mvc需要的组件进行注册,其中重要的部分有handlerMapping、handlerAdapter、interceptor、viewResolver等的注册。注册的类为MvcNamespaceHandler类,然后通过AnnotationDrivenBeanDefinitionParser类的parse()方法去解析标签。
2、组件注册完后,进入DispatcherServlet类进行第二步,组件的初始化,这边介绍handlerMapping的初始化,判断是否默认添加所有的HandlerMappings,如果是,则取出所有注册好的HandlerMapping,否则去context里边找是否有相关的bean,然后取出来,如果前两步都无法初始化handlerMapping,那么程序会给handlerMapping初始化一个默认的值。
3、初始化完成后,调用doDispatch()方法进行操作,首先通过handlerMapping.getHandler(request)的方法获取到handlerExcutionChain对象,然后通过getHandlerAdapter()获取handlerAdapter,传入handlerExcutionChain.getHandler()作为参数,之后调用handlerAdapter.handler()的方法获取ModelAndView,(包括模型数据、逻辑视图名),接着调用applyDefaultViewName(processedRequest, mv)方法获取view,再调用applyPostHandle(request,response,mv)方法,执行HandlerExecutionChain所有Interceptor中的postHandle方法,从这块代码看来,postHandle的执行时机是在执行完方法后没有把响应写入到response中执行的;最后调用 processDispatchResult(),处理请求,把参数都写入到request里面,进行渲染工作。
参考链接:
https://blog.csdn.net/sjjsh2/article/details/53100728
https://www.cnblogs.com/question-sky/p/7090875.html
https://blog.csdn.net/qq_38410730/article/details/79507465
https://blog.csdn.net/wangbiao007/article/details/50510274
https://www.cnblogs.com/davidwang456/p/4132215.html
https://www.cnblogs.com/he-px/p/7133240.html
https://blog.csdn.net/king_is_everyone/article/details/51446260