Spring源码学习【八】SpringMVC之HandlerMapping

目录

一、前言

二、源码学习

(一) 注册Bean

(二) 初始化策略

(三) 处理客户请求


一、前言

在前一篇中我们分析了DispatcherServlet的实现,在处理客户请求的doDispatch方法中提到了HandlerMapping,这是SpringMVC中请求控制的重要组件,用于将URL映射到Controller中。

首先,回忆一下平时使用的SpringMVC配置文件,通常会包括如下配置:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="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.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd ">

    <!-- 静态资源访问 -->
    <mvc:default-servlet-handler/>

    <!-- 启动注解 -->
    <mvc:annotation-driven/>

    <!-- 把标记了@Controller注解的类注册到IOC容器中 -->
    <context:component-scan base-package="com.greedystar.controller">
    </context:component-scan>

    <!-- ViewResolver Servlet、JSP视图解析-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 登录状态验证拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/search"/>
            <bean class="com.greedystar.interceptor.LoadInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

参考上一篇 Spring源码学习【八】SpringMVC之DispatcherServlet (一)初始化阶段 -> init() ,我们能够知道如下与HandlerMapping相关的内容:

  • DispatcherServlet初始化时会创建一个IOC容器,并将配置文件中配置的Bean注册到IOC容器中。
  • DispatcherServlet初始化时会初始化一系列Servlet需要的策略,如:initHandlerMappings、initHandlerAdapters 等。
  • DispatcherServlet处理客户请求时会通过HandlerMapping取得HandlerExecutionChain。

下面,让我们顺着这个思路看一看HandlerMapping的实现。

二、源码学习

(一) 注册Bean

DispatcherServlet初始化过程中会创建一个IOC容器,作为Spring 根IOC容器的子容器,并将配置文件中配置的Bean注册到容器中,代码可参考 Spring源码学习【八】SpringMVC之DispatcherServlet (一)初始化阶段 -> init()

熟悉SpringMVC的同学对上面的配置文件肯定很熟悉,但有两点需要特别注意:

1. <mvc:default-servlet-handler/> 会自动注册用于静态资源访问的Bean,如下:

<bean class="org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler"></bean>

2. <mvc:annotation-driven/> 会自动注册多个Bean,这些Bean是在使用SpringMVC注解(如@RequestMapping)时必须要的Bean(低版本的一些类已经在高版本中移除了,这里注册的是用于替代低版本过时类的Bean):

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter​​​​​​​"></bean>

<bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver​​​​​​​"></bean>

这里只列举了一部分,关于这部分大家可以参考<mvc:annotation-driven/>标签的解析器的实现,即:

org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser

(二) 初始化策略

回到DispatcherServlet中看一下策略初始化的实现,代码如下:

public class DispatcherServlet extends FrameworkServlet {

    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
        if (this.detectAllHandlerMappings) {
            // 从所有IOC容器中获取HandlerMapping,包括父容器
            Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<>(matchingBeans.values());
                // 将HandlerMappings排序,如上述中的RequestMappingHandlerMapping排在0位
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        else { // 从当前容器中获取HandlerMappings
            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.
            }
        }
        // 未找到配置的HandlerMapping,设置默认策略
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
            }
        }
    }

}

上面的代码中,从IOC容器中获取了配置的HandlerMapping Bean,由DispatcherServlet持有,并在未配置策略时使用默认策略,其余如HandlerAdapter的初始化过程大体一样就不在贴代码了。

到了这里我们发现DispatcherServlet已经持有了HandlerMapping,下一步就是在处理客户请求时调用了。

(三) 处理客户请求

回到DispatcherServlet中的doDispatch方法中,有如下代码:

mappedHandler = getHandler(processedRequest);

这句代码通过HandlerMapping获取请求匹配的处理器:一个HandlerExecutionChain类的实例,这个处理器中包含一个请求处理器和多个拦截器,下面来看一看这个过程:

public class DispatcherServlet extends FrameworkServlet {

    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            // 遍历所有HandlerMappings
            for (HandlerMapping hm : this.handlerMappings) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
                }
                // 取得一个处理器执行链
                HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }
}

上面的代码比较简单,就是遍历DispatcherServlet中的所有HandlerMapping,取得一个处理器后直接返回,我们需要重点关注一下这里是如何根据请求获取到匹配的处理器的:

HandlerMapping是一个接口,其中定义了getHandler方法,用于取得一个处理器,下面我们直接看一看这个方法的具体实现,在AbstractHandlerMapping中,代码如下:

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {

    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 尝试从http request中取得处理器,这里是一个模板方法,交由子类实现
        // 如AbstractUrlHandlerMapping中根据request的路径获取处理器
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            // 使用默认处理器
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // 取得的是一个Bean name
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }
        // 取得HandlerExecutionChain对象
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
        // 取得request路径
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        // 添加拦截器
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            }
            else {
                chain.addInterceptor(interceptor);
            }
        }
        return chain;
    }

}

到这里,就获取了一个匹配的处理器,接下来DispatcherServlet就可以通过这个处理器进行客户请求的处理了,客户请求的具体处理过程已经在上一篇中分析过了这里就不多赘述了。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值