spring源碼 — 4、MVC

spring mvc是怎麼實現的?爲何咱們只須要在方法上寫一個註解,就能夠經過http訪問這個接口?下面咱們分3部分來解答這兩個問題java

注意:本文是基於spring4.3.2的web

  • spring mvc總體流程
  • HandlerMapping
  • HandlerAdapter

spring mvc總體流程

咱們經過看一下spring處理一個http請求的過程來大概瞭解下spring

spring_mvc

Spring mvc的入口就是DispatcherServlet,請求交給這個servlet以後,經過調用doDispatch來分發這個請求,主要作了一下幾件事情spring-mvc

  • 處理multipart請求,若是有文件上傳等請求,先封裝爲DefaultMultipartHttpServletRequest
  • 從HandlerMapping中獲取處理該請求的handler,並構造HandlerExecutionChain,將入全部的interceptor
  • 根據handler從全部的HandlerAdapter中找到能夠處理這個handler的adapter
  • 執行全部interceptor.prehandle方法
  • 調用寫有RequestMapping註解的方法,返回ModelAndView
  • 執行全部interceptor.postHandle方法
  • 解析view
  • render,將model轉化爲response返回
  • 執行全部interceptor.afterCompletion
  • cleanupMultipart

從上面的流程中能夠看出,在處理過程當中主要是一些關鍵組件完成了對應的邏輯,下面咱們看下其中的組件。數據結構

HandlerMapping

做爲DispatcherServlet組件之一,就是其中一個fieldmvc

org.springframework.web.servlet.DispatcherServlet#handlerMappings

主要做用是維護從url到Controller的映射,根據url找到對應的Controller。app

有哪些HandlerMapping

spring是怎麼查找全部的HandlerMapping的呢?是在DispatcherServlet初始化的時候查找並初始化這個字段的cors

// org.springframework.web.servlet.DispatcherServlet#initHandlerMappings
private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;

    if (this.detectAllHandlerMappings) {
        // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        // 從容器中查找全部HandlerMapping類型的bean
        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");
        }
    }
}

那麼容器中有哪些HandlerMapping類型的bean呢?若是咱們使用RequestMapping註解的話須要在xml中進行如下配置post

<mvc:annotation-driven />

spring解析這個標籤使用的是this

org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser

這個類解析xml標籤的時候會向容器中註冊RequestMappingHandlerMapping,這個類繼承自HandlerMapping

若是咱們進行了一下配置

<mvc:default-servlet-handler />

spring解析這個標籤的使用的是

org.springframework.web.servlet.config.DefaultServletHandlerBeanDefinitionParser

解析的時候spring會向容器注入DefaultServletHttpRequestHandler和SimpleUrlHandlerMapping

那麼

  • BeanNameHandlerMapping
  • DefaultAnnotationHandlerMapping

因此這種狀況下會有這三個HandlerMapping類型的bean,spring遍歷handlerMappings來根據request path查找對應的handler。

HandlerMapping怎麼管理url到handler的映射關係

HandlerMapping抽象類AbstractHandlerMethodMapping中有一個field

// org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
private final MappingRegistry mappingRegistry = new MappingRegistry();

這個類就是用來維護url到handler的映射,看下這個類有哪些數據結構

// org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry
// 全部的mapping都會加入這個map中
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();
// 只有具體的(沒有通配符org.springframework.util.AntPathMatcher#isPattern)的纔會加入這個map中
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();
// 根據name找handler
private final Map<String, List<HandlerMethod>> nameLookup =
    new ConcurrentHashMap<String, List<HandlerMethod>>();
// cors mapping
private final Map<HandlerMethod, CorsConfiguration> corsLookup =

註冊mapping的過程就是講找到的mapping添加到上面對應的數據結構中,以RequestMappingHandlerMapping爲例,具體註冊的時機是在RequestMappingHandlerMapping bean初始化的時候,spring容器初始化完成之後會將容器中eagere init的bean進行初始化,構造bean的時候會調用afterPropertiesSet,最後會調用AbstractHandlerMethodMapping#initHandlerMethods來查找容器中全部的bean

  1. 找到容器中全部的bean
  2. 針對每一個bean查找裏面有沒有mapping

在調用到initHandlerMethods方法的時候會拿出容器中全部的bean,用isHandler判斷是不是handler,其實就是判斷是否有Controller或RequestMapping的註解

protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

找到類後,再找裏面的方法,方法的查找邏輯就是看看方法上有沒有RequestMapping,

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);
}

找到對應的方法以後構形成mapping註冊到MappingRegistry

public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        assertUniqueMethodMapping(handlerMethod, mapping);

        if (logger.isInfoEnabled()) {
            logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
        }
        // 全部的mapping都會加入mappingLookup
        this.mappingLookup.put(mapping, handlerMethod);

        List<String> directUrls = getDirectUrls(mapping);
        // 找到全部具體的(直接的,就是不包含通配符)的url添加到urlLookup
        for (String url : directUrls) {
            this.urlLookup.add(url, mapping);
        }

        String name = null;
        if (getNamingStrategy() != null) {
            name = getNamingStrategy().getName(handlerMethod, mapping);
            addMappingName(name, handlerMethod);
        }

        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
        if (corsConfig != null) {
            this.corsLookup.put(handlerMethod, corsConfig);
        }

        this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
    }
    finally {
        this.readWriteLock.writeLock().unlock();
    }
}

在根據url查找handler的時候優先查找urlLookup,看看沒有通配符的能不能匹配,若是沒有直接匹配的則再從mappingLookup裏面找

HandlerAdapter

HandlerAdapter是爲了將invoke過程的細節對於DispatcherServlet屏蔽,好比參數解析。

在DispatcherServlet初始化的時候調用inithandlerAdapters,在裏面找容器中全部的handlerAdapter實現類,容器裏面的handlerAdapter是在解析mvc標籤的時候加入容器的

總結

這篇文章經過spring處理一次請求的過程瞭解了springmvc怎麼工做,以及其中兩個關鍵組件,HandlerMapping和HandlerAdapter。

相關文章
相關標籤/搜索