[Spring MVC source code analysis] HandlerMapping

Posted by bhavin12300 on Sat, 12 Feb 2022 05:49:07 +0100

Members of the HandlerMapping family can be divided into two branches. One is inherited from AbastractUrlHandlerMapping, the other is inherited from AbastracHandlerMethodMapping, and both are inherited from AbastractHandlerMapping.

The green box AbstractHandlerMapping abstract class implements the skeleton logic of [obtain the processors and interceptors corresponding to the request], and then provides the getHandlerInternal(HttpServletRequest request) template method, which is implemented by the subclass.

Subclass of AbstractHandlerMapping:

The red box is "AbstractUrlHandlerMapping", which matches based on the URL. Of course, in actual development, this method is basically not used, and is replaced by @ RequestMapping # and other annotation methods. However, this method is still used for some path matching built-in in Spring MVC.

Yellow box: AbstractHandlerMethodMapping system, matching based on Method. For example, we are familiar with @ RequestMapping and other annotation methods.

The white box MatchableHandlerMapping interface defines the interface method to judge whether the request matches the specified {pattern} path.

AbstractHandlerMapping

AbstractHandlerMapping uses template mode to design the overall structure of HandlerMapping, and then subclasses can provide some initial values and specific algorithms by implementing the template method. AbstractHandlerMapping inherits WebApplicationObjectSupport. During initialization, the template method initApplicationContext will be called, that is, AbstractHandlerMapping is created in initApplicationContext.

// AbstractHandlerMapping.java

@Override
protected void initApplicationContext() throws BeansException {
    // Empty method. Give it to the subclass implementation, which is used to register custom interceptors into interceptors. At present, there is no subclass implementation.
    extendInterceptors(this.interceptors);
    // Scan the beans of registered mappedInterceptors and add them to mappedInterceptors
    detectMappedInterceptors(this.adaptedInterceptors);
    // Initialize interceptors to HandlerInterceptor type and add them to mappedInterceptors
    initInterceptors();
}

The detectmappedinterceptors (list < handlerinterceptor > mappedInterceptors) method scans the beans of registered mappedInterceptors and adds them to mappedInterceptors. The code is as follows:

// AbstractHandlerMapping.java

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
	// Scan the beans of registered mappedInterceptors and add them to mappedInterceptors
	// MappedInterceptor will match according to the request path and whether to intercept or not.
	mappedInterceptors.addAll(
			BeanFactoryUtils.beansOfTypeIncludingAncestors(
					obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}

initInterceptors() method, initialize 'interceptors' to HandlerInterceptor type and add it to' mappedInterceptors'. The code is as follows:

// AbstractHandlerMapping.java

protected void initInterceptors() {
    if (!this.interceptors.isEmpty()) {
        // Traversing the interceptors array
        for (int i = 0; i < this.interceptors.size(); i++) {
            // Get interceptor object
            Object interceptor = this.interceptors.get(i);
            if (interceptor == null) { // If it is empty, an IllegalArgumentException is thrown
                throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
            }
            // Initialize interceptors to HandlerInterceptor type and add them to mappedInterceptors
            // Note that the HandlerInterceptor does not need path matching and directly intercepts all
            this.adaptedInterceptors.add(adaptInterceptor(interceptor)); // <x>
        }
    }
}

You can see that AbstractHandlerMapping saves all interceptors, and its properties have two List type interceptors

/**
 * The interceptor array configured will not be used directly
 *
 * Instead, in the {@ link #initInterceptors()} method, initialize to {@ link #adaptedInterceptors} for use
 *
 * There are two ways to add:
 *
 * 1. {@link #setInterceptors(Object...)} method
 * 2. {@link #extendInterceptors(List)} method
 */
private final List<Object> interceptors = new ArrayList<>();

/**
 * The initialized interceptor HandlerInterceptor array matches the Url when used
 */
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();

After initialization, let's see how to get the Handler and Interceptor. getHandler(HttpServletRequest request) method to obtain the Handler executionchain object corresponding to the request. The code is as follows:

// DispatcherServlet.java

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        // Traversing the HandlerMapping array
        for (HandlerMapping mapping : this.handlerMappings) {
            // Get the HandlerExecutionChain object corresponding to the request
            HandlerExecutionChain handler = mapping.getHandler(request);
            // Get, return
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}
// AbstractHandlerMapping.java

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // <1> Get the processor. This method is an abstract method implemented by subclasses
    Object handler = getHandlerInternal(request);
    // <2> If not, the default processor is used
    if (handler == null) {
        handler = getDefaultHandler();
    }
    // <3> If it cannot be obtained, null is returned
    if (handler == null) {
        return null;
    }
    // Bean name or resolved handler?
    // <4> If the processor found is of String type, find the Bean type corresponding to String from the container as the processor.
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }

    // <5> Get HandlerExecutionChain object
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

    if (logger.isTraceEnabled()) {
        logger.trace("Mapped to " + handler);
    }
    else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
        logger.debug("Mapped to " + executionChain.getHandler());
    }

    if (CorsUtils.isCorsRequest(request)) {
        CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }

    // <7> Return    
    return executionChain;
}

getHandlerExecutionChain(Object handler, HttpServletRequest request) method to obtain the HandlerExecutionChain object. The code is as follows:

// AbstractHandlerMapping.java

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    // Create HandlerExecutionChain object
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

    // Get request path
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    // Traverse the adaptedInterceptors array to get the interceptor matching the request
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        // It needs to be matched. If the path matches, it will be added to the chain
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { // matching
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        // Without matching, it is directly added to the chain
        } else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

AbstractHandlerMethodMapping

AbstractHandlerMethodMapping implements the InitializingBean interface, inherits the AbstractHandlerMapping abstract class, takes' Method 'as the HandlerMapping abstract class of' Handler ', and provides general skeleton methods such as initialization and registration of Mapping. This is what we often call the template Method pattern.

// AbstractHandlerMethodMapping.java

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    /**
     * Mapping registry
     */
    private final MappingRegistry mappingRegistry = new MappingRegistry();

    /**
     * Mapping Naming strategy
     */
    @Nullable
    private HandlerMethodMappingNamingStrategy<T> namingStrategy;
}

Generic T represents a kind of condition specially used to match the Handler. The condition here is not only url, but also Request type (get, post), Request parameter, Head, etc. can be used as the condition to match the Handler. RequestMappingInfo is used by default.

MappingRegistry is the private class of AbstractHandlerMethodMapping and the Mapping registry.

// AbstractHandlerMethodMapping.java#MappingRegistry

/**
 * registry
 *
 * KEY: MappingInfo
 */
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

/**
 * The registry stores the correspondence between the matching condition and the HandlerMethod
 *
 * KEY: MappingInfo
 */
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

/**
 * The mapping of direct URLs saves the correspondence between URLs and matching conditions
 *
 * KEY: Direct URL
 * VALUE: MappingInfo array
 */
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

/**
 * MappingInfo Mapping of the name of to HandlerMethod
 *
 * KEY: Mapping Name of
 * VALUE: HandlerMethod array
 */
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

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

/**
 * Read write lock
 */
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

initialization

After knowing some key properties, let's take a look at its initialization. AbstractHandlerMapping implements the InitializingBean interface, and the spring container calls its afterpropertieset () method for initialization.

// AbstractHandlerMethodMapping.java

/**
 * Scan only accessible handlermethods
 */
private boolean detectHandlerMethodsInAncestorContexts = false;

@Override
public void afterPropertiesSet() {
	// <x> How to initialize the processor
	initHandlerMethods();
}

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #getCandidateBeanNames()
 * @see #processCandidateBean
 * @see #handlerMethodsInitialized
 */
protected void initHandlerMethods() {
	//Traverse beans and process them one by one
	for (String beanName : getCandidateBeanNames()) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			// < 1.2 > processing beans
			processCandidateBean(beanName);
		}
	}
	//Methods of initializing the processor. At present, it is an empty method, and there is no specific implementation yet
	handlerMethodsInitialized(getHandlerMethods());
}

getCandidateBeanNames() method, get the names of all beans, and then perform traversal processing.

// AbstractHandlerMethodMapping.java

/**
 * Determine the names of candidate beans in the application context.
 * @since 5.1
 * @see #setDetectHandlerMethodsInAncestorContexts
 * @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors
 */
protected String[] getCandidateBeanNames() {
	return (this.detectHandlerMethodsInAncestorContexts ? // Accessible
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
			obtainApplicationContext().getBeanNamesForType(Object.class));
}

processCandidateBean(String beanName) method to judge whether the Bean is a processor. If so, scan the processor method. The code is as follows:

// AbstractHandlerMethodMapping.java

/**
 * Determine the type of the specified candidate bean and call
 * {@link #detectHandlerMethods} if identified as a handler type.
 * <p>This implementation avoids bean creation through checking
 * {@link org.springframework.beans.factory.BeanFactory#getType}
 * and calling {@link #detectHandlerMethods} with the bean name.
 * @param beanName the name of the candidate bean
 * @since 5.1
 * @see #isHandler
 * @see #detectHandlerMethods
 */
protected void processCandidateBean(String beanName) {
    // <1> Get the type corresponding to the Bean
    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);
        }
    }
    // Judge whether the Bean is a processor. If so, scan the processor method
    if (beanType != null && isHandler(beanType)) { // <2.1>
        detectHandlerMethods(beanName); // <2.2>
    }
}

Call #ishandler (class <? > beantype) abstract method to judge whether the Bean type is processor. The specific implementation is in RequestMappingHandlerMapping

@Override
protected boolean isHandler(Class<?> beanType) {
        //Judge the @ Controller annotation on the class or the RequestMapping annotation on the method
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

detectHandlerMethods are methods to initialize the processor. The code is as follows:

// AbstractHandlerMethodMapping.java

/**
 * Look for handler methods in a handler.
 * @param handler the bean name of a handler or a handler instance
 */
protected void detectHandlerMethods(final Object handler) {
    // <1> Get processor type
    Class<?> handlerType = (handler instanceof String ?
            obtainApplicationContext().getType((String) handler) : handler.getClass());

    if (handlerType != null) {
        // <2> Get the real class. Because handlerType may be a proxy class
        final Class<?> userType = ClassUtils.getUserClass(handlerType);
        // <3> Get a collection of matching methods
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType)); // Abstract method, subclass implementation
        if (logger.isTraceEnabled()) {
            logger.trace("Mapped " + methods.size() + " handler method(s) for " + userType + ": " + methods);
        }
        // <4> Traverse methods and register HandlerMethod one by one
        methods.forEach((key, mapping) -> {
            Method invocableMethod = AopUtils.selectInvocableMethod(key, userType);
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

#Getmappingformethod (method method, class <? > handlertype) method to obtain the RequestMappingInfo object on the method, which is constructed based on @ RequestMapping \. The code is as follows:

// RequestMappingHandlerMapping.java

/**
 * Uses method and type-level @{@link RequestMapping} annotations to create
 * the RequestMappingInfo.
 * @return the created RequestMappingInfo, or {@code null} if the method
 * does not have a {@code @RequestMapping} annotation.
 * @see #getCustomMethodCondition(Method)
 * @see #getCustomTypeCondition(Class)
 */
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    // <1> Create a RequestMappingInfo object based on the @ RequestMapping annotation on the method
    RequestMappingInfo info = createRequestMappingInfo(method);
    if (info != null) {
        // <2> Based on the @ RequestMapping annotation on the class, merge it
        RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
        if (typeInfo != null) {
            info = typeInfo.combine(info);
        }
        // <3> If there is a prefix, set it to info
        String prefix = getPathPrefix(handlerType);
        if (prefix != null) {
            info = RequestMappingInfo.paths(prefix).build().combine(info);
        }
    }
    return info;
}

#Registerhandlermethod (object handler, method, t mapping) method, register HandlerMethod

// AbstractHandlerMethodMapping.java

/**
 * Register HandlerMethod
 *
 * Register a handler method and its unique mapping. Invoked at startup for
 * each detected handler method.
 * @param handler the bean name of the handler or the handler instance
 * @param method the method to register
 * @param mapping the mapping conditions associated with the handler method
 * @throws IllegalStateException if another method was already registered
 * under the same mapping
 */
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	this.mappingRegistry.register(mapping, handler, method);
}

// AbstractHandlerMethodMapping.java#MappingRegistry

public void register(T mapping, Object handler, Method method) {
    // <1> Obtain write lock
    this.readWriteLock.writeLock().lock();
    try {
        // < 2.1 > create HandlerMethod object
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        // < 2.2 > verify that the current mapping does not exist, otherwise an IllegalStateException will be thrown
        assertUniqueMethodMapping(handlerMethod, mapping);
        // < 2.3 > Add mapping + HandlerMethod to mappingLookup
        this.mappingLookup.put(mapping, handlerMethod);

        // < 3.1 > get the normal URL array corresponding to mapping
        List<String> directUrls = getDirectUrls(mapping);
        // < 3.2 > add to url + mapping to urlLookup set
        for (String url : directUrls) {
            this.urlLookup.add(url, mapping);
        }

        // <4> Initialize nameLookup
        String name = null;
        if (getNamingStrategy() != null) {
            // < 4.1 > get the name of Mapping
            name = getNamingStrategy().getName(handlerMethod, mapping);
            // < 4.2 > add the name of mapping + HandlerMethod to nameLookup
            addMappingName(name, handlerMethod);
        }

        // <5> TODO 1012 cors
        CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
        if (corsConfig != null) {
            this.corsLookup.put(handlerMethod, corsConfig);
        }

        // <6> Create a MappingRegistration object and add mapping + MappingRegistration to the registry
        this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod));
    } finally {
        // <7> Release write lock
        this.readWriteLock.writeLock().unlock();
    }
}

use

getHandlerInternal(ServerWebExchange exchange) method to obtain the HandlerMethod object corresponding to the request. The code is as follows:

// AbstractHandlerMethodMapping.java

/**
 * Look up a handler method for the given request.
 */
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // Get the requested path
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    // Acquire read lock
    this.mappingRegistry.acquireReadLock();
    try {
        //  Get HandlerMethod object
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        // Further, obtain the HandlerMethod object
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    } finally {
        // Release read lock
        this.mappingRegistry.releaseReadLock();
    }
}

Call #lookupHandlerMethod(ServerWebExchange exchange) method to obtain HandlerMethod object.

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    // <1> Store results that match the current request on the
    List<Match> matches = new ArrayList<>();
    // < 1.1 > priority, matching based on direct URL Mapping
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
        addMatchingMappings(directPathMatches, matches, request);
    }
    // < 1.2 > if you can't get the matching conditions directly by using louuppath, scan all the mappings in the registry for matching
    if (matches.isEmpty()) {
        // No choice but to go through all mappings...
        addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
    }

    // <2> If a Match is found, the handlerMethod property of the best matched Match object is obtained
    if (!matches.isEmpty()) {
        // < 2.1 > create a MatchComparator object and sort the matches results
        Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
        matches.sort(comparator);
        // < 2.2 > get the first Match object
        Match bestMatch = matches.get(0);
        // < 2.3 > handle cases where there are multiple Match objects!!
        if (matches.size() > 1) {
            if (logger.isTraceEnabled()) {
                logger.trace(matches.size() + " matching mappings: " + matches);
            }
            
            if (CorsUtils.isPreFlightRequest(request)) {
                return PREFLIGHT_AMBIGUOUS_MATCH;
            }
            // Compare bestMatch and secondBestMatch. If they are equal, there is a problem and an IllegalStateException is thrown
            // Because the two priorities are the same, it means that it is impossible to judge who has the highest priority
            Match secondBestMatch = matches.get(1);
            if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                Method m1 = bestMatch.handlerMethod.getMethod();
                Method m2 = secondBestMatch.handlerMethod.getMethod();
                String uri = request.getRequestURI();
                throw new IllegalStateException(
                        "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
            }
        }
        // < 2.4 > process the first Match object
        handleMatch(bestMatch.mapping, lookupPath, request);
        // < 2.5 > returns the handlerMethod attribute of the first Match object
        return bestMatch.handlerMethod;
    // <3> If no match is found, the mismatch is handled
    } else {
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
}

In the whole process, match is used as the carrier. Match is an internal class that encapsulates the matching condition and HandlerMothd attributes.

Addmatchingmappings (collection < T > mappings, list < match > matches, serverwebexchange exchange) method to match the current request with the Mapping in the registry. If the matching is successful, a Mapping record will be generated and added to # matches #. The code is as follows:

// AbstractHandlerMethodMapping.java

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, ServerWebExchange exchange) {
    // Traversing the Mapping array
	for (T mapping : mappings) {
	    // <1> Perform matching
		T match = getMatchingMapping(mapping, exchange);
		// <2> If there is a Match, a Match object is created and added to matches
		if (match != null) {
			matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
		}
	}
}

getMatchingMapping(T mapping, HttpServletRequest request) is implemented by the subclass RequestMappingInfoHandlerMapping

@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
		return info.getMatchingCondition(request);
}

RequestMappingInfo

// RequestMappingInfo.java

/**
 * name
 */
@Nullable
private final String name;

/**
 * Condition of request path
 */
private final PatternsRequestCondition patternsCondition;

/**
 * Condition of request method
 */
private final RequestMethodsRequestCondition methodsCondition;

/**
 * Condition of parameter
 */
private final ParamsRequestCondition paramsCondition;

/**
 * Condition of request header
 */
private final HeadersRequestCondition headersCondition;

/**
 * Conditions for consumable content type
 */
private final ConsumesRequestCondition consumesCondition;

/**
 * Conditions for producible content type
 */
private final ProducesRequestCondition producesCondition;

/**
 * Custom conditions
 */
private final RequestConditionHolder customConditionHolder;

#getMatchingCondition(HttpServletRequest request) method to obtain the matching condition from the current RequestMappingInfo. If it matches, a new RequestMappingInfo object is created based on its matching criteria. If there is no match, null is returned. The code is as follows:

@SuppressWarnings("Duplicates")
@Override
@Nullable
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
    // Match methodsCondition, paramsCondition, headercondition, consumesCondition, producesCondition
	RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
	ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
	HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
	ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
	ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
	// If any one is empty, null is returned, indicating that the matching failed
	if (methods == null || params == null || headers == null || consumes == null || produces == null) {
		return null;
	}

	// Match patternsCondition
	PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
	if (patterns == null) { // If patterns is empty, null will be returned, indicating that matching failed
		return null;
	}

	// Match customConditionHolder
	RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
	if (custom == null) { // If custom is empty, null will be returned, indicating that matching failed
		return null;
	}

	// Create a matching RequestMappingInfo object.
	// Why create a RequestMappingInfo object? Because of the current RequestMappingInfo object, a methodsCondition can configure GET, POST, DELETE and other conditions, but it actually matches a request type. At this time, the methods only represent the matching one.
	return new RequestMappingInfo(this.name, patterns,
			methods, params, headers, consumes, produces, custom.getCondition());
}

#compareTo(RequestMappingInfo other, HttpServletRequest request) method to compare priorities. The code is as follows:

// RequestMappingInfo.java

/**
 * Compares "this" info (i.e. the current instance) with another info in the context of a request.
 * <p>Note: It is assumed both instances have been obtained via
 * {@link #getMatchingCondition(HttpServletRequest)} to ensure they have conditions with
 * content relevant to current request.
 */
@Override
public int compareTo(RequestMappingInfo other, HttpServletRequest request) {
	int result;
	// Automatic vs explicit HTTP HEAD mapping
	// Special processing for HEAD request method
	if (HttpMethod.HEAD.matches(request.getMethod())) {
		result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
		if (result != 0) {
			return result;
		}
	}
	// Compare patternsCondition
	result = this.patternsCondition.compareTo(other.getPatternsCondition(), request);
	if (result != 0) {
		return result;
	}
	// Compare paramsCondition
	result = this.paramsCondition.compareTo(other.getParamsCondition(), request);
	if (result != 0) {
		return result;
	}
	// Compare headersCondition
	result = this.headersCondition.compareTo(other.getHeadersCondition(), request);
	if (result != 0) {
		return result;
	}
	// Compare consumesCondition
	result = this.consumesCondition.compareTo(other.getConsumesCondition(), request);
	if (result != 0) {
		return result;
	}
	// Compare productscondition
	result = this.producesCondition.compareTo(other.getProducesCondition(), request);
	if (result != 0) {
		return result;
	}
	// Implicit (no method) vs explicit HTTP method mappings
	// Compare methodsCondition
	result = this.methodsCondition.compareTo(other.getMethodsCondition(), request);
	if (result != 0) {
		return result;
	}
	// Compare customConditionHolder
	result = this.customConditionHolder.compareTo(other.customConditionHolder, request);
	if (result != 0) {
		return result;
	}
	return 0;
}

Although the code is very long, it actually calls the #compareTo(RequestMethodsRequestCondition other, HttpServletRequest request) method corresponding to each attribute one by one according to the priority until it is not equal

 

 

 

 

Topics: Spring Spring MVC source code