SpringMvc RequestMappingHandlerMapping


 RequestMappingHandlerMapping是SpringMvc中一個比較核心的類,查看下它的類結構圖:  

          

InitializingBean是個很神奇的接口,在Spring每個容器的bean構造方法、屬性設置之后,會先調用InitializingBean接口的afterPropertiesSet方法;

RequestMappingHandlerMapping的afterPropertiesSet方法: 初始化了config對象,以及調用父類AbstractHandlerMethodMapping的afterPropertiesSet,父類方法afterPropertiesSet 邏輯是 initHandlerMethods,這也是SpringMvc初始化尋找Controller以及映射加載的核心邏輯; 

@Override public void afterPropertiesSet() { this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setPathHelper(getUrlPathHelper()); this.config.setPathMatcher(getPathMatcher()); this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); this.config.setTrailingSlashMatch(this.useTrailingSlashMatch); this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch); this.config.setContentNegotiationManager(getContentNegotiationManager());      // 初始化config對象,主要屬性就是pathMatcher; 以及調用父類 afterPropertiesSet 方法,這是SpringMvc映射關系加載的核心; super.afterPropertiesSet(); }

 

AbstractHandlerMethodMapping 的 initHandlerMethods代碼:

protected void initHandlerMethods() { if (logger.isDebugEnabled()) { logger.debug("Looking for request mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class));  
      // detectHandlerMethodsInAncestorContexts 默認為false,代表不會檢測SpringMvc父容器中的bean的映射關系                                                
for (String beanName : beanNames) {                     
//遍歷容器中的beanName, 代理的對象跳過,獲取當前bean的類型,調用isHandler判斷是否是處理器(handler\controller)
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null; try { beanType = getApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } if (beanType != null && isHandler(beanType)) { //isHandler方法判斷是否是controller,判斷邏輯下面有; detectHandlerMethods(beanName);  //加載Controller和請求映射關系 } } } handlerMethodsInitialized(getHandlerMethods()); // 該方法是個空實現 }

 

isHandler方法: 判斷當前bean的class屬性,標注了Controller或者RequestMapping注解,就會去加載Controller和請求映射關系,如果不是handler,迭代下一個bean對象;

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

 

 

detectHandlerMethods方法:

protected void detectHandlerMethods(final Object handler) { Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());//之前傳入handler為string類型,此處去容器獲取handler的class final Class<?> userType = ClassUtils.getUserClass(handlerType); //處理class為CGLIB生成class,如果是CGLIB的獲取父類class Map<Method, T> methods = MethodIntrospector.selectMethods(userType, new MethodIntrospector.MetadataLookup<T>() { @Override public T inspect(Method method) { return getMappingForMethod(method, userType); } }); if (logger.isDebugEnabled()) { logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods); } for (Map.Entry<Method, T> entry : methods.entrySet()) { Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); T mapping = entry.getValue(); registerHandlerMethod(handler, invocableMethod, mapping); } }

 

 

MethodIntrospector.selectMethods(..)方法是個很全面的解析方法:注釋寫得很詳細,☆方法處,metadataLookup.inspect方法,往上看,調用的就是getMappingForMethod方法獲取RequestMappingInfo對象;
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) { final Map<Method, T> methodMap = new LinkedHashMap<Method, T>(); Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>(); Class<?> specificHandlerType = null; if (!Proxy.isProxyClass(targetType)) {        
//handler class不是JDK代理生成的,加入到handlerTypes集合,specificHandlerType為當前handler class handlerTypes.add(targetType); specificHandlerType
= targetType; } handlerTypes.addAll(Arrays.asList(targetType.getInterfaces())); /
/handler class實現的接口加入到handlerTypes
for (Class<?> currentHandlerType : handlerTypes) { final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType); ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
//該工具類方法,遍歷了該currentHandlerType本類中所有的方法 // 調用的是 getDeclaredMethods(),然后遍歷method數組,調用doWith回調處理method方法
public void doWith(Method method) { Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); T result = metadataLookup.inspect(specificMethod);              
// ☆ 核心!!! 這里處理了方法以及類上的映射關系,並且返回泛型T,實際類型是RequesMappingInfo
if (result != null) { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) { methodMap.put(specificMethod, result); } } } }, ReflectionUtils.USER_DECLARED_METHODS);  
// ReflectionUtils.USER_DECLARED_METHODS是個methodFilter,作用是過濾方法是用戶定義、且非橋接類型的方法;
}
return methodMap; }

 

RequestMappingHandlerMapping 的 getMappingForMethod 方法:先分析方法上的映射關系,再分析類所在方法上的映射關系,然后結合處理;

下面一點點記錄我查看這個方法的發現;

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = createRequestMappingInfo(method);        
// 解析類上RequestMapping注解
if (info != null) { RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);// 解析方法上@RequestMapping注解 if (typeInfo != null) { info = typeInfo.combine(info); //方法上RequestMapping注解不為空,就需要結合分析 } } return info; }

 

createRequestMappingInfo 方法:

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
    //調用Spring注解工具類AnnotatedElementUtils獲取方法上注解 RequestMapping requestMapping
= AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class<?> ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); 
    //RequestMappingHandlerMapping兩個方法都是返回null,空實現
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }

 

具體的RequestMappingInfo的構造采用建造者模式還是其他模式的?

protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, RequestCondition<?> customCondition) {  //customCondition一般都為null   return RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))// @RequestMapping(path={....}) 將path屬性設置上去 .methods(requestMapping.method())     // @RequestMapping(method={....}) 將method屬性設置上去 .params(requestMapping.params())   // @RequestMapping(method={....}) 將method屬性設置上去 .headers(requestMapping.headers())     // @RequestMapping(headers={....}) 將headers屬性設置上去 .consumes(requestMapping.consumes())   // @RequestMapping(consumes={....}) 將consumes屬性設置上去 .produces(requestMapping.produces())   // @RequestMapping(produces={....}) 將produces屬性設置上去 .mappingName(requestMapping.name())    // @RequestMapping(name={....}) 將name屬性設置上去    .customCondition(customCondition) .options(this.config) .build(); }

 

 這里只分析一個開頭、一個結尾這樣;

RequestMappingInfo 的 paths 方法:

    public static Builder paths(String... paths) { // paths是@RequestMapping的path屬性,字符串數組,這里用可變參數來接收,效果一樣   
      return new DefaultBuilder(paths); }

 

 Builder接口所有方法都返回Builder對象,DefaultBuilder持有一堆屬性,可以看到都是@ReuqestMapping的屬性;

paths方法就將注解的path屬性注入到DefaultBuilder中,其他方法methods、params、headers、consumes、produces、mappingName、customCondition都是這個套路;

而 options注入的config屬性 ,最開始 afterPropertiesSet 里 ,this.config = new RequestMappingInfo.BuilderConfiguration();

就是將RequestMappingHandleMapping中的config作為DefaultBuilder的options注入; 最后就是build方法。

                           

DefaultBuilder 的 build方法:

    public RequestMappingInfo build() { ContentNegotiationManager manager = this.options.getContentNegotiationManager();        // PatternsRequestCondition構造的主要屬性就是paths,代表了映射的路徑,不以/開頭會添加 / 這個開頭 PatternsRequestCondition patternsCondition = new PatternsRequestCondition( this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(), this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(), this.options.getFileExtensions()); return new RequestMappingInfo(this.mappingName, patternsCondition, new RequestMethodsRequestCondition(methods), new ParamsRequestCondition(this.params), new HeadersRequestCondition(this.headers), new ConsumesRequestCondition(this.consumes, this.headers), new ProducesRequestCondition(this.produces, this.headers, manager), this.customCondition);  // customCondition通常為null }

 

build方法返回 RequestMappingInfo,其中構造入參都是XXXRequestCondition這種,他們都實現了RequestCondition接口;

private PatternsRequestCondition(Collection<String> patterns, UrlPathHelper urlPathHelper, PathMatcher pathMatcher, boolean useSuffixPatternMatch, boolean useTrailingSlashMatch, List<String> fileExtensions) {     //這里就是 prependLeadingSlash 會判斷 @RequestMapping注解的 path屬性,不是以 /開頭會添加 / this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));   
     
this.pathHelper = (urlPathHelper != null ? urlPathHelper : new UrlPathHelper()); this.pathMatcher = (pathMatcher != null ? pathMatcher : new AntPathMatcher()); this.useSuffixPatternMatch = useSuffixPatternMatch; this.useTrailingSlashMatch = useTrailingSlashMatch; if (fileExtensions != null) { for (String fileExtension : fileExtensions) { if (fileExtension.charAt(0) != '.') { fileExtension = "." + fileExtension; } this.fileExtensions.add(fileExtension); } } }

 

private static Set<String> prependLeadingSlash(Collection<String> patterns) { if (patterns == null) { return Collections.emptySet(); } Set<String> result = new LinkedHashSet<String>(patterns.size()); for (String pattern : patterns) { if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) { //URL不以 /開頭就會自動添加 / pattern = "/" + pattern; } result.add(pattern); } return result; }

 

  回到RequestMappingInfo的構造方法,將@RequestMapping的所有屬性都以 RequestCondition的實現類 形式保存到  RequestMappingInfo對象中;

接口RequestCondition定義了三個方法,1.combine:一般用來 方法級別@RequestMapping與類級別@RequestMapping結合,返回新的(通常是RequestMappingInfo);   

2.getMatchingCondition:檢查request對象是否滿足條件,返回一個新的滿足條件的RequestMappingInfo實例(T泛型用都是RequestMappingInfo);

 3.compareTo  用來多個匹配的情況排序挑選最合適的 

public interface RequestCondition<T> {

    T combine(T other);

    T getMatchingCondition(HttpServletRequest request);

    int compareTo(T other, HttpServletRequest request);

}

 

至此 回到 RequestMappingHandlerMapping 的 getMappingForMethod方法 ,第一個方法級別的createRequestMappingInfo方法分析完畢,下面兩行解析了標注在 類上的 注解,並且返回 RequestMappingInfo對象,

第188行就是類上標注了@RequestMapping注解,和方法上同樣標注@RequestMapping結合處理的步驟:調用類上的RequestMappingInfo的combine方法

               

查看RequestMappingInfo對象的combine方法:

public RequestMappingInfo combine(RequestMappingInfo other) {
    // RequestMapping的name屬性的處理方法,一般name屬性很少寫,處理方式:兩個都不為空就返回this.name#other.name;有一個為空 就返回另外一個name   String name
= combineNames(other);  
    //下面邏輯A分析 調用AntPathMatcher的combine方法,將類上URL和方法上URL組合並放入新PatternsRequestCondition     PatternsRequestCondition patterns
= this.patternsCondition.combine(other.patternsCondition);
    //下面邏輯B分析,並且接下來的methods、params、headers等等實現方式大體一致 RequestMethodsRequestCondition methods
= this.methodsCondition.combine(other.methodsCondition); ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition); HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
    //!!comsume和produce判斷邏輯不是相加,方法上的該屬性優先級高於類級別上的 ConsumesRequestCondition consumes
= this.consumesCondition.combine(other.consumesCondition); ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition); RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder); return new RequestMappingInfo(name, patterns,//返回一個新的RequestMappingInfo對象,其中所有RequestCondition都是新創建的對象 methods, params, headers, consumes, produces, custom.getCondition()); }

 

邏輯A: PatternsRequestCondition 之前介紹過,其屬性patterns 就是@RequestMapping的path / value 屬性的集合,且判斷 path是否以  / 開頭,如果不是會自動補全  / 開頭;

其實現了RequestCondition接口,查看其combine方法

    public PatternsRequestCondition combine(PatternsRequestCondition other) {
          // result作為新的請求路徑集合 Set
<String> result = new LinkedHashSet<String>();   
          //類上注解@RequestMapping path不為空,方法上注解注解@RequestMapping path不為空      

            //此處的AntPathMatcher就是RequestMappingHandlerMapping對象里的antPathMatcher對象
            //@RequestMapping path屬性是集合類型的,這類似笛卡爾積形式 調用AntPathMatcher的combine方式,進行URL組合 加入到result
    if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) {
for (String pattern1 : this.patterns) {            
        
for (String pattern2 : other.patterns) {         result.add(this.pathMatcher.combine(pattern1, pattern2)); } } }
    //已經說明有一方為空了,只要判斷另外一方是否為空,不為空直接加入Set<String>
else if (!this.patterns.isEmpty()) {     result.addAll(this.patterns); } else if (!other.patterns.isEmpty()) { result.addAll(other.patterns); } else { result.add(""); } /返回了一個新的PatternsRequestCondition對象,patterns屬性就是當前方法的請求路徑 return new PatternsRequestCondition(result, this.pathHelper, this.pathMatcher, this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions); / }

 

 

邏輯A-1:AntPathMatcher對象如何對請求路徑進行結合combine? 

  類上path  方法上path 結合后path
null null  
/hotels null /hotels
null /hotels /hotels
/hotels /bookings /hotels/bookings
/hotels bookings /hotels/bookings
/hotels/* /bookings /hotels/bookings
/hotels/** /bookings /hotels/**/bookings
/hotels {hotel} /hotels/{hotel}
/hotels/* {hotel} /hotels/{hotel}
/hotels/** {hotel} /hotels/**/{hotel}
/*.html hotels.html /hotels.html
/*.html /hotels /hotels.html
/*.html /*.txt IllegalArgumentException

 

 邏輯B:RequestMethodsRequestCondition 的 combine 方法,方法上注解@RequestMapping的method加入到類上注解的method屬性里,然后返回一個全新的RequestMethodsRequestCondition,持有新的method集合;

public RequestMethodsRequestCondition combine(RequestMethodsRequestCondition other) { Set<RequestMethod> set = new LinkedHashSet<RequestMethod>(this.methods); set.addAll(other.methods); return new RequestMethodsRequestCondition(set); }

 

 getMappingForMethod方法調用結束,返回結合后的RequestMappingInfo對象; 回到MethodIntrospector.selectMethods方法,第19行就是調用的getMappingForMethod方法,返回RequestMappingInfo對象result,result不為空之后,

會篩選不是橋接方法,存入methodMap這個Map對象,key-type是Method,value-type是RequestMappingInfo類型;

該方法selectMethods將Controller / Handler中所有方法都進行判斷加載請求映射,返回methodMap對象;

 1     public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {  2         final Map<Method, T> methodMap = new LinkedHashMap<Method, T>();  3         Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();  4         Class<?> specificHandlerType = null;  5 
 6         if (!Proxy.isProxyClass(targetType)) {  7  handlerTypes.add(targetType);  8             specificHandlerType = targetType;  9  } 10  handlerTypes.addAll(Arrays.asList(targetType.getInterfaces())); 11 
12         for (Class<?> currentHandlerType : handlerTypes) { 13             final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType); 14 
15             ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() { 16  @Override 17                 public void doWith(Method method) { 18                     Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass); 19                     T result = metadataLookup.inspect(specificMethod); 20                     if (result != null) { 21                         Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); 22                         if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) { 23  methodMap.put(specificMethod, result); 24  } 25  } 26  } 27  }, ReflectionUtils.USER_DECLARED_METHODS); 28  } 29 
30         return methodMap; 31     }

 

回到最開始的分析detectHandlerMethods方法:methods對象就是上面返回的methodMap,如果日志設置了DEBUG,每遍歷一個controller都會輸出日志;

 1 protected void detectHandlerMethods(final Object handler) {  2         Class<?> handlerType = (handler instanceof String ?
 3  getApplicationContext().getType((String) handler) : handler.getClass());  4         final Class<?> userType = ClassUtils.getUserClass(handlerType);  5 
 6         Map<Method, T> methods = MethodIntrospector.selectMethods(userType,  7                 new MethodIntrospector.MetadataLookup<T>() {  8  @Override  9                     public T inspect(Method method) { 10                         return getMappingForMethod(method, userType); 11  } 12  }); 13 
14         if (logger.isDebugEnabled()) { 15             logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods); 16  } 17         for (Map.Entry<Method, T> entry : methods.entrySet()) {//遍歷methods,並且調用registerHandlerMethod注冊映射信息 18             Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType); 19             T mapping = entry.getValue(); 20  registerHandlerMethod(handler, invocableMethod, mapping); 21  } 22     }

 

registerHandlerMethod:總結 :

 

RequestMappingHandlerMapping.mappingRegistry屬性

 

  key-type value-type
mappingLookup
RequestMappingInfo HandlerMethod對象
urlLookup
請求路徑URL RequestMappingInfo
nameLookup controller name中大寫字母#方法名(如UC#test) HandlerMethod對象
registry
RequestMappingInfo
MappingRegistration對象(持有
RequestMappingInfo\HandlerMethod\URL路徑\name)

 

protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); } // this對象指RequestMappingHandlerMapping,mapping是RequestMappingInfo對象,handler是controler的name,method是當前@RequestMapping方法 public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); //可重入鎖 寫鎖上鎖 這里不太明白為什么要上鎖 try {
          //創建新的HandlerMethod對象 下面邏輯C 介紹HandlerMethod 邏輯D 分析createHandlerMethod方法 HandlerMethod handlerMethod
= createHandlerMethod(handler, method);
          //校驗唯一性,一個RequestMappingInfo對應一個Handlermethod,如果根據RequestMappingInfo找到不同的hm 拋出異常 assertUniqueMethodMapping(handlerMethod, mapping); //INFO級別日志 比如Mapped "{[/user/test]}" onto public java.lang.String demo2.UserController.test(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
if (logger.isInfoEnabled()) { logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod); }
           //this指RequestMappingHandlerMapping.MappingRegistry,mappingLookup保存着RequestMappingInfo--HandlerMethod對象
this.mappingLookup.put(mapping, handlerMethod);            //獲取 mapping 的PatternsRequestCondition的patterns,也就是拼接的URL路徑,並且路徑不包含* ?的就加入到集合返回 , List<String> directUrls = getDirectUrls(mapping);
         
for (String url : directUrls) {
            //MappingRegistry的urlLookup保存着 url--RequestMappingInfo對象
this.urlLookup.add(url, mapping);   } String name = null; if (getNamingStrategy() != null) {
             //name屬性感覺沒用,如果@RequestMapping有name屬性就是這個屬性 如果沒有就是 controller名字中的大寫字母#方法名字,比如UC#test name
= getNamingStrategy().getName(handlerMethod, mapping);
              //MappingRegistry的nameLookup保存着 name--HandlerMethod集合 addMappingName(name, handlerMethod); } CorsConfiguration corsConfig
= initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); }
     //MappingRegistry的registry保存着RequestMappingInfo--MappingRegistration,MappingRegistration幾乎有映射的所有信息
this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); //可重入鎖 寫鎖 釋放鎖 } }

 

邏輯C:HandlerMethod對象 屬性有bean,就是controller對象實例;beanFactory當前Spring容器;beanType就是controller的類型;method就是handler method;birdgeMethod是handler method的橋接方法;MethodParameter是handler method的方法參數,handlerMethod一般為null;

  HandlerMethod,作用Spring給出了:一個handler method對象,包含了method以及controller對象,此外提供了便捷方式獲取方法入參、返回值、注解等等;

          

 

 邏輯D:createHandlerMethod方法只是調用了HandlerMethod的構造方法,構造方法中對方法入參進行了處理;

 1     protected HandlerMethod createHandlerMethod(Object handler, Method method) {  2  HandlerMethod handlerMethod;  3         if (handler instanceof String) {  4             String beanName = (String) handler;  5             handlerMethod = new HandlerMethod(beanName,  6  getApplicationContext().getAutowireCapableBeanFactory(), method);  7  }  8         else {  9             handlerMethod = new HandlerMethod(handler, method); 10  } 11         return handlerMethod; 12  } 13 
14 public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) { 15         Assert.hasText(beanName, "Bean name is required"); 16         Assert.notNull(beanFactory, "BeanFactory is required"); 17         Assert.notNull(method, "Method is required"); 18         this.bean = beanName;            //controller beanName 19         this.beanFactory = beanFactory;      //當前controller所在Spring工廠 20         this.beanType = ClassUtils.getUserClass(beanFactory.getType(beanName));  //獲取當前controller類型 21         this.method = method;            //當前handler method 22         this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); //查找method的橋接方法,沒有橋接方法就是返回自身 23         this.parameters = initMethodParameters(); //初始化MethodParameter對象 設置了每個MethodParameter的method、parameterIndex屬性 具體方法下圖 24         this.resolvedFromHandlerMethod = null; 25     }

                                       

至此,registerHandlerMethod方法分析完畢,detectHandlerMethods方法分析完成,

Spring主要做了哪些工作:將所有請求映射關系保存到上面RequestMappingHandlerMapping的mappingRegistry的相關屬性中,詳情見上面表格。

 

分析過SpringMvc的請求流程  SpringMvc流程

  篇幅太長,只分析如何找根據請求找到對應的handler?  遍歷HandlerMapping對象,調用其getHanlder方法查找controller / handler , RequestMappingHandlerMapping對象的父類AbstractHandlerMapping實現了getHandler方法,方法最開始Object handler = getHandlerInternal(request); 那么我們從AbstractHandlerMapping 的 getHandlerInternal開始記錄.

 1 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
       // 根據request請求路徑以及servlet-mapping得到要請求URL
2 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); 3 if (logger.isDebugEnabled()) { 4 logger.debug("Looking up handler method for path " + lookupPath); 5 } 6 this.mappingRegistry.acquireReadLock();                        //讀鎖 上鎖    7 try {
           // 這里就是MVC尋找controller匹配的方法! 下面花大篇幅介紹下
8 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
9
if (logger.isDebugEnabled()) { 10 if (handlerMethod != null) { 11 logger.debug("Returning handler method [" + handlerMethod + "]"); 12 } 13 else { 14 logger.debug("Did not find handler method for [" + lookupPath + "]"); 15 } 16 }
          //找到handlerMethod,但bean是controller beanName,用beanFactory getBean替換bean
17 return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); 18 } 19 finally { 20 this.mappingRegistry.releaseReadLock(); 21 } 22 }

 lookupHandlerMethod方法:

  邏輯是這樣的,先根據請求的URL 從 RequestMappingHandlerMapping的mappingRegistryurlLookup中嘗試尋找RequestMappingInfo;

  尋找大致分為兩種情況:一種請求URL清楚,不需要通配符比對,那肯定可以直接找到RequestMappingInfo集合,創建Match對象並且添加到集合里面,然后根據規則對Match集合排序選出最優解;

     第二種情況URL帶有通配符,那需要遍歷映射關系再重復第一種情況。

 1 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {  2         List<Match> matches = new ArrayList<Match>();
        //return this.urlLookup.get(urlPath);調用mappingRegistry的urlLookup根據URL尋找RequestMappingInfo
3      List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
4
if (directPathMatches != null) {     
          //遍歷找到的RequestMappingInfo集合, 然后尋找匹配的對象並處理添加到matches集合,見 邏輯E 分析    
5 addMatchingMappings(directPathMatches, matches, request);
6
} 7 if (matches.isEmpty()) {   //matches為空,有可能是因為通配符匹配的情況需要再次匹配 8 // No choice but to go through all mappings... 9 addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request); 10 } 11 12 if (!matches.isEmpty()) {                                     
          //返回一個MatchComparator對象
          // 持有Comparator屬性,並且compare方法是調用了RequestMappingInfo的compareTo
13 Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));         
           //說到底排序還是調用了RequestMappingInfo的compareTo方法, 也存在優先級之分 URL路徑>params>headers>comsume>produce>method 排序分析見文章最后
14 Collections.sort(matches, comparator); 15 if (logger.isTraceEnabled()) { 16 logger.trace("Found " + matches.size() + " matching mapping(s) for [" + 17 lookupPath + "] : " + matches); 18 } 19 Match bestMatch = matches.get(0); //找到最優匹配 20 if (matches.size() > 1) { 21 if (CorsUtils.isPreFlightRequest(request)) { 22 return PREFLIGHT_AMBIGUOUS_MATCH; 23 } 24 Match secondBestMatch = matches.get(1); 25 if (comparator.compare(bestMatch, secondBestMatch) == 0) { //存在兩個匹配且相等 拋出異常 26 Method m1 = bestMatch.handlerMethod.getMethod(); 27 Method m2 = secondBestMatch.handlerMethod.getMethod(); 28 throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" + 29 request.getRequestURL() + "': {" + m1 + ", " + m2 + "}"); 30 } 31 } 32 handleMatch(bestMatch.mapping, lookupPath, request); //解析URL變量,完成設置request屬性等工作 33 return bestMatch.handlerMethod;               //返回最優匹配的HandlerMethod對象 34 } 35 else { //沒找到handlerMethod 就返回null 36 return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request); 37 } 38 }

 

邏輯E:遍歷找到的RequestMappingInfo集合,調用RequestMappingInfo的getMatchCondition進行匹配以獲取匹配的RequestMappingInfo對象;尋找到合適的RequestMappingInfo對象之后,創建一個Match對象加入matches集合;

mappingRegistry 的 getMappings方法返回mappingLookup屬性,上述表格mappingLookup 存放 RequestMappingInfo--HandlerMethod,根據RequestMappingInfo對象從map中取對象,(邏輯G分析 RequestMappingInfo重寫了的hashCode以及equals方法)。 Match對象持有RequestMappingInfo以及HandlerMethod屬性;此處方法調用結束matches可能包含多個Match結果;

           

RequestMappingInfo的getMatchingCondition方法

 1 public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
  //如果RequestMappingInfo沒有指定methods屬性,返回RequestMappingInfo本身,否則方法匹配
2 RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
        //下面幾個匹配邏輯是一樣的,匹配了返回自身,沒匹配返回null,具體參數作用、如何匹配看吧;
3 ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request); 4 HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request); 5 ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request); 6 ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request); 7         //有一個條件匹配不上就直接返回null 8 if (methods == null || params == null || headers == null || consumes == null || produces == null) {
9
return null; 10 } 11         //其他匹配上了,最重要的匹配請求URL, 路徑匹配作為 邏輯F 分析 12 PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
13 if (patterns == null) { 14 return null; 15 } 16 17 RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request); 18 if (custom == null) { 19 return null; 20 } 21 22 return new RequestMappingInfo(this.name, patterns,
             //尋找到匹配之后,構造一個新的RequestMappingInfo對象,持有上述匹配之后的結果返回
23 methods, params, headers, consumes, produces, custom.getCondition()); 24 }

 

邏輯F:PatternsRequestCondition匹配

調用PatternsRequestCondition 的 getMatchingPattern 方法進行URL匹配;遍歷PatternsRequestCondition的 patterns屬性,逐個getMatchingPattern進行比較,匹配上將pattern存入集合,並且使用AntPatternComparator進行排序,排序之后集合加入到一個新的PatternsRequestCondition對象中;

  //pattern就是patterns屬性當前迭代的元素,lookupPath就是servlet-mapping下請求URL
1
private String getMatchingPattern(String pattern, String lookupPath) { 2 if (pattern.equals(lookupPath)) {   //兩者相等 無疑義直接返回   這種是沒有通配符 * ?這種都會很容易匹配到並且返回       3 return pattern; 4 } 5 if (this.useSuffixPatternMatch) { // useSuffixPatternMatch默認為true 6 if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) { // fileExtensions默認為空 7 for (String extension : this.fileExtensions) { 8 if (this.pathMatcher.match(pattern + extension, lookupPath)) { 9 return pattern + extension; 10 } 11 } 12 } 13 else { 14 boolean hasSuffix = pattern.indexOf('.') != -1; //pattern字符串是否有 . 15 if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) { //沒有 . 就用AntPathMatcher的match匹配 pattern.* lookupPath 16 return pattern + ".*"; 17 } 18 } 19 } 20 if (this.pathMatcher.match(pattern, lookupPath)) {
      // 用AntPathMatcher的match匹配 pattern lookupPath,匹配上就返回pattern
21 return pattern; 22 } 23 if (this.useTrailingSlashMatch) { 24 if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) { 25 return pattern +"/"; 26 } 27 } 28 return null; 29 }

 

邏輯G:先介紹下為什么要看RequestMappingInfo的hashCode以及equals方法?RequestMappingInfo作為key存儲在Map中,肯定需要重寫HashCode以及equals方法;

    RequestMappingInfo的hashCode以及equals方法:  比較的時候會先調用hashCode判斷值是否相等,相等再比較equals方法,如果相等則認為是同一個對象;

    先來看hashCode方法,將RequestMappingInfo的所有RequestCondition屬性按公式求和,這些屬性都是AbstractRequestCondition,equals和hashCode方法都調用了getContent方法,而AbstractRequestCondition的各種實現類的getContent方法,比如PatternsRequestCondition實現方式就是返回patterns(URL)集合;比如RequestMethodsRequestCondition實現就是返回methods集合;

 RequestMappingInfo
1
public boolean equals(Object other) { 2 if (this == other) { 3 return true; 4 } 5 if (!(other instanceof RequestMappingInfo)) { 6 return false; 7 } 8 RequestMappingInfo otherInfo = (RequestMappingInfo) other; 9 return (this.patternsCondition.equals(otherInfo.patternsCondition) && 10 this.methodsCondition.equals(otherInfo.methodsCondition) && 11 this.paramsCondition.equals(otherInfo.paramsCondition) && 12 this.headersCondition.equals(otherInfo.headersCondition) && 13 this.consumesCondition.equals(otherInfo.consumesCondition) && 14 this.producesCondition.equals(otherInfo.producesCondition) && 15 this.customConditionHolder.equals(otherInfo.customConditionHolder)); 16 } 17 18 @Override 19 public int hashCode() { 20 return (this.patternsCondition.hashCode() * 31 + // primary differentiation 21 this.methodsCondition.hashCode() + this.paramsCondition.hashCode() + 22 this.headersCondition.hashCode() + this.consumesCondition.hashCode() + 23 this.producesCondition.hashCode() + this.customConditionHolder.hashCode()); 24 }

 

AbstractRequestCondition  
1
public boolean equals(Object obj) { 2 if (this == obj) { 3 return true; 4 } 5 if (obj != null && getClass() == obj.getClass()) { 6 AbstractRequestCondition<?> other = (AbstractRequestCondition<?>) obj; 7 return getContent().equals(other.getContent()); 8 } 9 return false; 10 } 11 12 @Override 13 public int hashCode() { 14 return getContent().hashCode(); 15 }

 

 

 分析到上面,getHandlerInternal已經找到了對應的HandlerMethod對象,調用getHandlerExecutionChain封裝成HandlerExecutionChain;

 1 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {  2         HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
 3                 (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); 
           // //構造一個HandlerExecutionChain對象,持有handlerMethod
4 5 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); 6 for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
      //adaptedInterceptors在開啟<mvc:annotation-drvien/>之后不為空,多了一個MappedInterceptor攔截器
7 if (interceptor instanceof MappedInterceptor) { 8 MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; 9 if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { 10 chain.addInterceptor(mappedInterceptor.getInterceptor());     
      //將ConversionServiceExposingInterceptor添加到HandlerExecutionChain的interceptorList屬性中
11 } 12 } 13 else { 14 chain.addInterceptor(interceptor); 15 } 16 } 17 return chain; //返回HandlerExecutionChain對象 18 }

 

Tip1:這個RequestMappingHandlerMapping的MappedInterceptor是從哪里注入的呢?

開啟了<mvc:annotation-driven />之后 Spring向容器中注入了這樣兩個bean的定義,MappedInterceptor,該對象持有ConversionServiceExposingInterceptor對象;

       

容器中有了MappedInterceptor對象,什么時候給RequestMappingHandlerMapping設置的adaptedInterceptors呢?通過打斷點分析到,RequestMappingHandlerMapping實現了ApplicationContextAware接口,Spring向其注入ApplicationContext的時候,調用了initApplicationContext方法,不斷進入方法最后進入到父類AbstractHandlerMapping的initApplicationContext方法,

 1 protected void initApplicationContext() throws BeansException {  2         extendInterceptors(this.interceptors);  3         detectMappedInterceptors(this.adaptedInterceptors); //此處添加了RequestMappingHandlerMapping的adaptedInterceptors  4  initInterceptors();  5 }  6 
 7 protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
       //這里將容器中的MappedInterceptor添加到了RequestMappingHandlerMapping的adaptedInterceptors
8 mappedInterceptors.addAll(                   
9
BeanFactoryUtils.beansOfTypeIncludingAncestors( 10 getApplicationContext(), MappedInterceptor.class, true, false).values()); 11 }

 

至此,如何找到HandlerMethod已經分析完畢;

 

總結

SpringMvc請求尋找規則 : 如果一個請求同時匹配上多個方法,按照如下順序選擇執行哪個方法:

先URL匹配的方法 >>>>>  params滿足的方法  >>>>>  headers 滿足的方法  >>>>>>consume滿足的方法 >>>> produce 滿足的方法 >>>> method滿足的方法

 

如果一個請求匹配上了多個RequestMappingInfo篩選:

之前介紹過排序是調用 RequestMappingInfo的compareTo進行排序

 1 public int compareTo(RequestMappingInfo other, HttpServletRequest request) {  2         int result = this.patternsCondition.compareTo(other.getPatternsCondition(), request); //優先URL進行匹配  3         if (result != 0) {  4             return result;  5  }  6         result = this.paramsCondition.compareTo(other.getParamsCondition(), request);  7         if (result != 0) {  8             return result;  9  } 10         result = this.headersCondition.compareTo(other.getHeadersCondition(), request); 11         if (result != 0) { 12             return result; 13  } 14         result = this.consumesCondition.compareTo(other.getConsumesCondition(), request); 15         if (result != 0) { 16             return result; 17  } 18         result = this.producesCondition.compareTo(other.getProducesCondition(), request); 19         if (result != 0) { 20             return result; 21  } 22         result = this.methodsCondition.compareTo(other.getMethodsCondition(), request); 23         if (result != 0) { 24             return result; 25  } 26         result = this.customConditionHolder.compareTo(other.customConditionHolder, request); 27         if (result != 0) { 28             return result; 29  } 30         return 0; 31     }

 

 介紹下URL如何排序吧,其他類似; 假設兩個URL   /get1  可以被匹配  /get*  以及 /get?

 1 public int compareTo(PatternsRequestCondition other, HttpServletRequest request) {  2         String lookupPath = this.pathHelper.getLookupPathForRequest(request);  3         Comparator<String> patternComparator = this.pathMatcher.getPatternComparator(lookupPath); //獲取AntPatternComparator比較器  4         Iterator<String> iterator = this.patterns.iterator();  5         Iterator<String> iteratorOther = other.patterns.iterator();  6         while (iterator.hasNext() && iteratorOther.hasNext()) {  7             int result = patternComparator.compare(iterator.next(), iteratorOther.next()); //URL比較規則在這里  8             if (result != 0) {  9                 return result; 10  } 11  } 12         if (iterator.hasNext()) { 13             return -1; 14  } 15         else if (iteratorOther.hasNext()) { 16             return 1; 17  } 18         else { 19             return 0; 20  } 21     }

 

 URL比較規則:按照請求URL通配符按一定權重計算排序順序,{個數+*個數+ ** 個數 ;所以 get* 比get?排在前面;

 1 public int compare(String pattern1, String pattern2) { //例子中pattern1為 /get* pattern2為/get?  2             PatternInfo info1 = new PatternInfo(pattern1); //具體查看下面構造方法  3             PatternInfo info2 = new PatternInfo(pattern2);  4 
 5             if (info1.isLeastSpecific() && info2.isLeastSpecific()) {  6                 return 0;  7  }  8             else if (info1.isLeastSpecific()) {  9                 return 1; 10  } 11             else if (info2.isLeastSpecific()) {  //上面三種情況是 比較 /**的情況 12                 return -1; 13  } 14 
15             boolean pattern1EqualsPath = pattern1.equals(path); 16             boolean pattern2EqualsPath = pattern2.equals(path); 17             if (pattern1EqualsPath && pattern2EqualsPath) { 18                 return 0; 19  } 20             else if (pattern1EqualsPath) { 21                 return -1; 22  } 23             else if (pattern2EqualsPath) {  //這三種情況是比較 pattern1 pattern2存在和請求URL完全匹配的情況 24                 return 1; 25  } 26 
27             if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) { 28                 return 1; 29  } 30             else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) { //哪個pattern的 /**多 哪個排在前面 31                 return -1; 32  } 33 
34             if (info1.getTotalCount() != info2.getTotalCount()) { 35                 return info1.getTotalCount() - info2.getTotalCount();  //按照權重來排序了 {算1 *算1 **算2 哪個大哪個排前面 /get*權重為1排前面 36  } 37 
38             if (info1.getLength() != info2.getLength()) { 39                 return info2.getLength() - info1.getLength(); 40  } 41 
42             if (info1.getSingleWildcards() < info2.getSingleWildcards()) { 43                 return -1; 44  } 45             else if (info2.getSingleWildcards() < info1.getSingleWildcards()) { 46                 return 1; 47  } 48 
49             if (info1.getUriVars() < info2.getUriVars()) { 50                 return -1; 51  } 52             else if (info2.getUriVars() < info1.getUriVars()) { 53                 return 1; 54  } 55 
56             return 0; 57  } 58 
59 public PatternInfo(String pattern) { 60                 this.pattern = pattern; 61                 if (this.pattern != null) { 62  initCounters(); 63                     this.catchAllPattern = this.pattern.equals("/**");  //代表匹配所有就是pattern為 /** 64                     this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**"); 65  } 66                 if (this.uriVars == 0) { 67                     this.length = (this.pattern != null ? this.pattern.length() : 0); 68  } 69  } 70 
71             protected void initCounters() { 72                 int pos = 0; 73                 while (pos < this.pattern.length()) { 74                     if (this.pattern.charAt(pos) == '{') { //存在變量 則uriVars自增 75                         this.uriVars++; 76                         pos++; 77  } 78                     else if (this.pattern.charAt(pos) == '*') { //解析到* 79                         if (pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') { 80                             this.doubleWildcards++;  // doubleWildcards代表有兩個*的 81                             pos += 2; 82  } 83                         else if (pos > 0 && !this.pattern.substring(pos - 1).equals(".*")) { //最后一位是* 且倒數第二位不是 * 84                             this.singleWildcards++; // singleWildcards代表有單個* 85                             pos++; 86  } 87                         else { 88                             pos++; 89  } 90  } 91                     else { 92                         pos++; 93  } 94  } 95             }

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM