HandlerMapping之RequestMappingHandlerMapping初始化

一、HandlerMapping類圖

默認情況下,SpringMVC 將加載當前系統中所有實現了HandlerMapping 接口的bean。如果只期望SpringMVC加載指定的handlermapping 時,可以修改web.xml中的DispatcherServlet的初始參數,將detectAllHandlerMappings的值設置爲false:

<init-param>

<param-name>detectAllHandlerMappings</param-name>

<param-value> false</param-value>

</ init-param>

此時Spring MVC將查找名爲“ handlerMapping”的bean, 並作爲當前系統中唯一的handlermapping。如果沒有定義handlerMapping 的話,則SpringMVC將按照org. Springframework.web.servlet.,DispatcherServlet所在目錄下的DispatcherServlet.properties中所定義的org.Springframework.web.servlet.HandlerMapping的內容來加載默認的handlerMapping (用戶沒有自定義Strategies的情況下)。

image.png

 

二、初始化HandlerMethod

2.1 初始化HandlerMethod流程

(1) 獲取容器中所有的Bean,篩選出類上有@Controller或@RequestMapping註解的類,作爲候選Handler

(2) 遍歷候選Bean所有方法,如果方法上沒有@RequestMapping註解則忽略該方法,否則獲取方法和類上的@RequestMapping註解併合並,創建RequestMappingInfo實例

(3) 註冊RequestMappingInfo與HandlerMethod映射信息:

將Handler和Method封裝到HandlerMethod中,建立RequestMappingInfo --> HandlerMethod映射關係

建立directUrl --> List<RequestMappingInfo>映射關係,同一個URL處理的請求方法不同:Get、Post等

建立name --> List<RequestMappingInfo>映射關係

建立HandlerMethod --> CorsConfiguration映射關係

建立RequestMappingInfo --> MappingRegistration映射關係,MappingRegistration封裝了RequestMappingInfo、HandlerMethod、directUrls、name

 

2.2 RequestMappingHandlerMapping

2.2.1 afterPropertiesSet

從類圖中可知RequestMappingHandlerMapping實現InitializingBean接口,下面看afterPropertiesSet方法

public void afterPropertiesSet() {

 // 用於請求映射目的配置選項的容器。 創建RequestMappingInfo實例需要這種配置,但是通常在所有RequestMappingInfo實例中使用。

 this.config = new RequestMappingInfo.BuilderConfiguration();

 this.config.setUrlPathHelper(getUrlPathHelper());

 this.config.setPathMatcher(getPathMatcher());

 this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);

 this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);

 this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);

 this.config.setContentNegotiationManager(getContentNegotiationManager());

 

 super.afterPropertiesSet();

}

 

2.2.2 getMappingForMethod

// 獲取方法上的@RequestMapping註解,並創建RequestMappingInfo。調用者傳送門:2.3.5

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {

 RequestMappingInfo info = createRequestMappingInfo(method);

 if (info != null) {

  RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);

  if (typeInfo != null) {

   info = typeInfo.combine(info);

  }

  String prefix = getPathPrefix(handlerType);

  if (prefix != null) {

   info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);

  }

 }

 return info;

}

 

2.2.3 根據@RequestMapping創建RequestMappingInfo createRequestMappingInfo

根據@RequestMapping註解創建RequestMappingInfo時,有getCustomTypeCondition、getCustomMethodCondition這兩個方法值得我們關注,可以擴展RequestMappingHandlerMapping實現@RequestMapping註解上的自定義RequestCondition。那麼RequestCondition在哪裏會用到呢?傳送門:4.1

 

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {

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

}

 

protected RequestMappingInfo createRequestMappingInfo(

  RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

 

 RequestMappingInfo.Builder builder = RequestMappingInfo

   .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))

   .methods(requestMapping.method())

   .params(requestMapping.params())

   .headers(requestMapping.headers())

   .consumes(requestMapping.consumes())

   .produces(requestMapping.produces())

   .mappingName(requestMapping.name());

 if (customCondition != null) {

  builder.customCondition(customCondition);

 }

 return builder.options(this.config).build();

}

 

2.3 AbstractHandlerMethodMapping

2.3.1 afterPropertiesSet

public void afterPropertiesSet() {

 initHandlerMethods();

}

 

2.3.2 initHandlerMethods

protected void initHandlerMethods() {

 for (String beanName : getCandidateBeanNames()) {

  if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {

   processCandidateBean(beanName);

  }

 }

 // 輸出總計包括檢測到的映射+通過registerMapping的顯式註冊

 handlerMethodsInitialized(getHandlerMethods());

}

 

2.3.3 獲取候選Bean名稱 getCandidateBeanNames

 

 

// 在應用程序上下文中確定候選bean的名稱

protected String[] getCandidateBeanNames() {

 return (this.detectHandlerMethodsInAncestorContexts ?

   BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :

   obtainApplicationContext().getBeanNamesForType(Object.class));

}

 

 

2.3.4 處理候選Bean processCandidateBean

 

// 確定指定的候選bean的類型,如果標識爲handler類型,則調用 {@link #detectHandlerMethods}

protected void processCandidateBean(String beanName) {

 Class<?> beanType = null;

 try {

  beanType = obtainApplicationContext().getType(beanName);

 }

 catch (Throwable ex) {

  // An unresolvable bean type, probably from a lazy bean - let's ignore it.

  if (logger.isTraceEnabled()) {

   logger.trace("Could not resolve type for bean '" + beanName + "'", ex);

  }

 }

 // isHandler由子類實現,類上有@Controller或@RequestMapping註解

 if (beanType != null && isHandler(beanType)) {

  detectHandlerMethods(beanName);

 }

}

 

2.3.5 查找Handler中處理請求方法 detectHandlerMethods

// 在指定的handler bean中查找處理程序方法。{@link #detectHandlerMethods}避免了bean的創建。

protected void detectHandlerMethods(Object handler) {

 Class<?> handlerType = (handler instanceof String ?

   obtainApplicationContext().getType((String) handler) : handler.getClass());

 

 if (handlerType != null) {

  Class<?> userType = ClassUtils.getUserClass(handlerType);

  Map<Method, T> methods = MethodIntrospector.selectMethods(userType,

    (MethodIntrospector.MetadataLookup<T>) method -> {

     try {

      // 子類實現獲取方法或類上的@RequestMapping,封裝爲RequestMappingInfo。方法上有@RequestMapping時纔會獲取類上的@RequestMapping。傳送門:2.2.2

      return getMappingForMethod(method, userType);

     }

     catch (Throwable ex) {

      throw new IllegalStateException("Invalid mapping on handler class [" +

        userType.getName() + "]: " + method, ex);

     }

    });

  if (logger.isTraceEnabled()) {

   logger.trace(formatMappings(userType, methods));

  }

  methods.forEach((method, mapping) -> {

   // 在目標類型上選擇一個可調用的方法:給定方法本身(如果實際在目標類型上公開),或者在目標類型的一個接口或目標類型本身上對應的方法。

   Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);

   registerHandlerMethod(handler, invocableMethod, mapping);

  });

 }

}

 

2.3.6 註冊Handler中處理請求方法 registerHandlerMethod

protected void registerHandlerMethod(Object handler, Method method, T mapping) {

 this.mappingRegistry.register(mapping, handler, method);

}

 

2.4 HandlerMethod註冊容器 MappingRegistry

2.4.1 註冊HandlerMethod register

// RequestMappingInfo --> MappingRegistration<RequestMappingInfo>
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

// RequestMappingInfo --> HandlerMethod

private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

// url(@RequestMapping註解上配置的) --> RequestMappingInfo

private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

// name(HandlerMethodMappingNamingStrategy生成) --> List<HandlerMethod>

private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

// HandlerMethod --> CorsConfiguration

private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

 

public void register(T mapping, Object handler, Method method) {

  this.readWriteLock.writeLock().lock();

  try {

   // 創建HandlerMethod實例,封裝了handler、method

   HandlerMethod handlerMethod = createHandlerMethod(handler, method);

   assertUniqueMethodMapping(handlerMethod, mapping);

   this.mappingLookup.put(mapping, handlerMethod);

 

   // 獲取@RequestMapping註解上配置的非正則表達式路徑

   List<String> directUrls = getDirectUrls(mapping);

   for (String url : directUrls) {

    this.urlLookup.add(url, mapping);

   }

 

   String name = null;

   if (getNamingStrategy() != null) {

    name = getNamingStrategy().getName(handlerMethod, mapping);

    addMappingName(name, handlerMethod);

   }

 

   // 方法或類上的@CrossOrigin註解,封裝成CorsConfiguration,由子類實現

   CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);

   if (corsConfig != null) {

    this.corsLookup.put(handlerMethod, corsConfig);

   }

 

   this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));

  }

  finally {

   this.readWriteLock.writeLock().unlock();

  }

 }

 

2.4.2 校驗映射關係是否唯一 assertUniqueMethodMapping

private void assertUniqueMethodMapping(HandlerMethod newHandlerMethod, T mapping) {

  HandlerMethod handlerMethod = this.mappingLookup.get(mapping);

  if (handlerMethod != null && !handlerMethod.equals(newHandlerMethod)) {

   throw new IllegalStateException(

     "Ambiguous mapping. Cannot map '" + newHandlerMethod.getBean() + "' method \n" +

     newHandlerMethod + "\nto " + mapping + ": There is already '" +

     handlerMethod.getBean() + "' bean method\n" + handlerMethod + " mapped.");

  }

 }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章