HandlerMapping组件的作用解析一个个Request请求,并找到相应处理这个Request的Handler。Handler一般可以理解为Controller控制器里的一个方法。
HandlerMapping组件主要做了两件事件。
在SpringMvc的源码中,HandlerMapping定义为一个接口。接口除了定义几个属性字段,只定义了一个getHandler方法。


从以上类图中可以看出,HandlerMapping组件主要是分了两个系列。一个系列主要继承至AbstractHandlerMethodMapping。另一个系列主要继承至AbstractUrlHandlerMapping。而AbstractHandlerMethodMapping和AbstractUrlHandlerMapping这两个抽象类又都是继承至AbstractHandlerMapping。
AbstractHandlerMapping是一个抽象类,它实现了HandlerMapping接口。AbstractHandlerMapping是一个非常基础的类,HandlerMapping的所有子类系列都是继承自它。AbstractHandlerMapping采用了模板模式进行了整体的设计,各个子类通过覆写模板方法来实现相应功能。
AbstractHandlerMappin抽象类既然继承了HandlerMapping接口,它肯定事要实现getHandler方法。在AbstractHandlerMappin类中,具体代码如下:
- @Override
-
- @Nullable
-
- public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
-
- Object handler = getHandlerInternal(request);
-
- if (handler == null) {
-
- handler = getDefaultHandler();
-
- }
-
- if (handler == null) {
-
- return null;
-
- }
-
- // Bean name or resolved handler?
-
- if (handler instanceof String) {
-
- String handlerName = (String) handler;
-
- handler = obtainApplicationContext().getBean(handlerName);
-
- }
-
-
-
- // Ensure presence of cached lookupPath for interceptors and others
-
- if (!ServletRequestPathUtils.hasCachedPath(request)) {
-
- initLookupPath(request);
-
- }
-
-
-
- HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
-
-
-
- if (logger.isTraceEnabled()) {
-
- logger.trace("Mapped to " + handler);
-
- }
-
- else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
-
- logger.debug("Mapped to " + executionChain.getHandler());
-
- }
-
-
-
- if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
-
- CorsConfiguration config = getCorsConfiguration(handler, request);
-
- if (getCorsConfigurationSource() != null) {
-
- CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
-
- config = (globalConfig != null ? globalConfig.combine(config) : config);
-
- }
-
- if (config != null) {
-
- config.validateAllowCredentials();
-
- }
-
- executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
-
- }
-
-
-
- return executionChain;
-
- }
getHandler方法中要特别注意getHandlerInternal(request)方法,该方法是一个模板方法。在AbstractHandlerMapping类中,getHandlerInternal(request)方法只是一个抽象的方法,没有做任何的事情。该方法专门预留给AbstractHandlerMapping的子类来覆写,从而实现自己的业务逻辑。
AbstractHandlerMapping还继承自WebApplicationObjectSupport类,并重写了该父类的initApplicationContext方法。
- @Override
-
- protected void initApplicationContext() throws BeansException {
-
- extendInterceptors(this.interceptors);
-
- detectMappedInterceptors(this.adaptedInterceptors);
-
- initInterceptors();
-
- }
在initApplicationContext方法,定义了三个方法。
extendInterceptors是一个模板方法,给子类提供了一个修改this.interceptors拦截器的入口。
detectMappedInterceptors方法是将Spring MVC中所有MappedInterceptor类型的bean,添加到this.adaptedInterceptors的集合中。
initInterceptors是初始化拦截器,将所有的this.interceptors集合中的拦截器包装后,添加到this.adaptedInterceptors的集合中。
AbstractHandlerMethodMapping是一个非常重要的类,AbstractHandlerMethodMapping除了继承AbstractHandlerMapping抽象类,它还实现了InitializingBean接口。
Handler的注册
在Spring中如果一个类实现了InitializingBean接口,Spring容器就会在实例化该Bean时,会调用Bean的afterPropertiesSet方法。
AbstractHandlerMethodMapping类中,在覆写InitializingBean接口的afterPropertiesSet方法时,完成了初始化注册的工作。这也是HandlerMapping组件的第一步工作,把Request请求和对应的Handler先进行注册。
AbstractHandlerMethodMapping类的afterPropertiesSet方法具体代码如下图所示。
- @Override
-
- public void afterPropertiesSet() {
-
- initHandlerMethods();
-
- }
可以看出在AbstractHandlerMethodMapping类的afterPropertiesSet方法中,调用了initHandlerMethods()方法。看initHandlerMethods()方法的名称,就知道它其实是完成了初始化的工作。
- protected void initHandlerMethods() {
-
- for (String beanName : getCandidateBeanNames()) {
-
- if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
-
- processCandidateBean(beanName);
-
- }
-
- }
-
- handlerMethodsInitialized(getHandlerMethods());
-
- }
在initHandlerMethods()方法中,通过getCandidateBeanNames方法,先获取Spring容器中所有的Bean的名称。过滤掉名称以” scopedTarget.”打头的Bean。通过循环再把Bean的名称传入processCandidateBean(beanName)方法。
processCandidateBean(beanName)方法主要做了三件事情。
detectHandlerMethods方法
detectHandlerMethods方法主要做了两件事情。
Handler的查找
在AbstractHandlerMethodMapping类中,覆写了父类AbstractHandlerMapping的模板方法getHandlerInternal方法。
- protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
-
- String lookupPath = initLookupPath(request);
-
- this.mappingRegistry.acquireReadLock();
-
- try {
-
- HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
-
- return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
-
- }
-
- finally {
-
- this.mappingRegistry.releaseReadLock();
-
- }
-
- }
在getHandlerInternal方法中,主要做了三件事件。
RequestMappingInfoHandlerMapping类主要是重写了父类的getMatchingMapping方法。getMatchingMapping方法会根据当前的请求,返回一个匹配了各种RequestCondition的RequestMappingInfo对象。
SpringMVC会根据这个RequestMappingInfo对象来获取对应的Handler。
Spring MVC容器在初始化HandlerMapping类型的组件时,最终初始化的AbstractHandlerMethodMapping系列组件,就是RequestMappingHandlerMapping。
RequestMappingHandlerMapping主要是重写了父类的三个方法。
重写了父类的初始化方法,在afterPropertiesSet方法中,创建了一个BuilderConfiguration类型的对象。然后对BuilderConfiguration对象,进行了属性的设置。
主要是用于判断获取何种类型的Handler。对Handler起到一个过滤的作用,只取@Controller和@RequestMapping两种注解类型的Handler。
getMappingForMethod方法主要是通过method来创建相应的RequestMappingInfo对象。程序先从method对象上获取RequestMapping注解。再从RequestMapping注解的信息,来创建“路径匹配”, ”头部匹配”, ”请求参数匹配”, ”可产生MIME匹配”, ”可消费MIME匹配”, ”请求方法匹配”,等RequestCondition接口的实例。最后组合这些RequestCondition接口实例,来创建一个RequestMappingInfo对象。SpringMVC会根据RequestMappingInfo对象来注册请求和Handler的映射关系。
RequestMappingInfo对象实现了RequestCondition接口。接口RequestCondition是Spring MVC对一个请求匹配条件的概念建模。
AbstractUrlHandlerMapping系列从名字就可以看出,主要是处理url和handler的关系。AbstractUrlHandlerMapping类先将url和handler的映射关系存在一个Map。再通过url来获取相应的handler。
AbstractUrlHandlerMapping类跟AbstractHandlerMethodMapping类一样,也继承了AbstractHandlerMapping这个抽象类。所有url跟HandlerMapping相关系列的子类,都是继承至AbstractUrlHandlerMapping这个父类。
AbstractUrlHandlerMapping同时也实现了MatchableHandlerMapping的接口。MatchableHandlerMapping接口定义了一个用于匹配的match方法。
HandlerMap的注册
在AbstractUrlHandlerMapping类中,registerHandler这个方法是专门负责对handler进行注册的。Handler的注册,其实就是把url和对应的handler存储到handlerMap这个哈希map中。
- 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;
-
-
-
- // Eagerly resolve handler if referencing singleton via name.
-
- if (!this.lazyInitHandlers && handler instanceof String) {
-
- String handlerName = (String) handler;
-
- ApplicationContext applicationContext = obtainApplicationContext();
-
- if (applicationContext.isSingleton(handlerName)) {
-
- resolvedHandler = applicationContext.getBean(handlerName);
-
- }
-
- }
-
-
-
- Object mappedHandler = this.handlerMap.get(urlPath);
-
- if (mappedHandler != null) {
-
- if (mappedHandler != resolvedHandler) {
-
- throw new IllegalStateException(
-
- "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
-
- "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
-
- }
-
- }
-
- else {
-
- if (urlPath.equals("/")) {
-
- if (logger.isTraceEnabled()) {
-
- logger.trace("Root mapping to " + getHandlerDescription(handler));
-
- }
-
- setRootHandler(resolvedHandler);
-
- }
-
- else if (urlPath.equals("/*")) {
-
- if (logger.isTraceEnabled()) {
-
- logger.trace("Default mapping to " + getHandlerDescription(handler));
-
- }
-
- setDefaultHandler(resolvedHandler);
-
- }
-
- else {
-
- this.handlerMap.put(urlPath, resolvedHandler);
-
- if (getPatternParser() != null) {
-
- this.pathPatternHandlerMap.put(getPatternParser().parse(urlPath), resolvedHandler);
-
- }
-
- if (logger.isTraceEnabled()) {
-
- logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
-
- }
-
- }
-
- }
-
- }
在registerHandler方法中,主要做了4件事情。
Handler的查找
在AbstractUrlHandlerMapping类中,具体实现了如何从一个url来获取相应的的handler。AbstractUrlHandlerMapping类中,重写了getHandlerInternal方法。通过url来获取handler的逻辑,就写在getHandlerInternal方法中。
- protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
-
- String lookupPath = initLookupPath(request);
-
- Object handler;
-
- if (usesPathPatterns()) {
-
- RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
-
- handler = lookupHandler(path, lookupPath, request);
-
- }
-
- else {
-
- handler = lookupHandler(lookupPath, request);
-
- }
-
- if (handler == null) {
-
- // We need to care for the default handler directly, since we need to
-
- // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
-
- Object rawHandler = null;
-
- if (StringUtils.matchesCharacter(lookupPath, '/')) {
-
- rawHandler = getRootHandler();
-
- }
-
- if (rawHandler == null) {
-
- rawHandler = getDefaultHandler();
-
- }
-
- if (rawHandler != null) {
-
- // Bean name or resolved handler?
-
- if (rawHandler instanceof String) {
-
- String handlerName = (String) rawHandler;
-
- rawHandler = obtainApplicationContext().getBean(handlerName);
-
- }
-
- validateHandler(rawHandler, request);
-
- handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
-
- }
-
- }
-
- return handler;
-
- }
在getHandlerInternal方法中,程序会先根据request获取到一个url。再通过lookupHandler方法来获取相应的Handler。
lookupHandler方法
lookupHandler从方法名称就可以知道,就是通过url来查找对应的Handler。lookupHandler首先会从handlerMap中直接获取。若找到了Handler,则会直接返回。
若不能直接从handlerMap中获取,则会使用PathPattern进行模式匹配。如果一个url匹配了多个PathPattern,会对多个PathPattern进行排序,取最优的一个。
获取到了PathPattern后,通过PathPattern从pathPatternHandlerMap获取Handler。如果Handler为String类型,那么这个Handler是Handle Bean的名称。再根据这个BeanName在Spring容器中找到相应的Bean。
获取到Handler后,会使用validateHandler对这个Handler进行校验。validateHandler是一个模板方法,主要留给子类进行扩展实现。
最后会使用buildPathExposingHandler方法,为这个Handler增加一些拦截器。
buildPathExposingHandler
buildPathExposingHandler方法主要是给Handler注册了两个内部的拦截器。他们分别是PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor拦截器。
在AbstractDetectingUrlHandlerMapping类中,主要是重写了父类的initApplicationContext()方法。在initApplicationContext()方法中,调用了detectHandlers()方法。
- protected void detectHandlers() throws BeansException {
-
- ApplicationContext applicationContext = obtainApplicationContext();
-
- if (logger.isDebugEnabled()) {
-
- logger.debug("Looking for URL mappings in application context: " + applicationContext);
-
- }
-
- String[] beanNames = (this.detectHandlersInAncestorContexts ?
-
- BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
-
- applicationContext.getBeanNamesForType(Object.class));
-
-
-
- // Take any bean name that we can determine URLs for.
-
- for (String beanName : beanNames) {
-
- String[] urls = determineUrlsForHandler(beanName);
-
- if (!ObjectUtils.isEmpty(urls)) {
-
- // URL paths found: Let's consider it a handler.
-
- registerHandler(urls, beanName);
-
- }
-
- else {
-
- if (logger.isDebugEnabled()) {
-
- logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
-
- }
-
- }
-
- }
-
- }
在调用了detectHandlers()方法中,主要做了以下几个步骤。
determineUrlsForHandler(beanName)方法在AbstractDetectingUrlHandlerMapping类中,只是一个虚方法,专门留给子类来具体实现。
Spring MVC容器在初始化HandlerMapping类型的组件时,默认初始化AbstractUrlHandlerMapping系列的组件时,初始化的就是BeanNameUrlHandlerMapping组件。
BeanNameUrlHandlerMapping类继承至AbstractDetectingUrlHandlerMapping这个父类,子类中主要是重写了determineUrlsForHandler方法。determineUrlsForHandler方法中,主要是筛选了Bean的名称或者Bean的别名以“/”斜杠开头的Bean。最后会把Bean的名称和对应的Bean对象注册到handlerMap这个HashMap对象中。
SimpleUrlHandlerMapping类继承至AbstractUrlHandlerMapping这个父类。SimpleUrlHandlerMapping类中有一个Map
除了Map