Introduction to the
Based on the initial exploration of the request path in the previous part, we have learned a rough path from a request to a specific processing method. This article will continue to explore how the path matches the processing method
An overview of
Based on previous article: A Primer on Spring Web Requests
The key code for the request path to the handler is roughly located in the class dispatcherservlet.java
The specific code is as follows:
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
Copy the code
MappedHandler is the key, so how do we get the mappedHandler of the request
The source code parsing
Get mappedHandler
In class: dispatcherServlet.java, we locate the key code that mappedHandler gets
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
Copy the code
From the above code, we can see that the fetch is done by request, and we can also notice that it is null, which is the classic 404 code
/**
* No handler found -> set appropriate HTTP response status.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception if preparing the response failed
*/
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
pageNotFoundLogger.warn("No mapping for " + request.getMethod() + "" + getRequestUri(request));
}
if (this.throwExceptionIfNoHandlerFound) {
throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
new ServletServerHttpRequest(request).getHeaders());
}
else{ response.sendError(HttpServletResponse.SC_NOT_FOUND); }}Copy the code
Let’s go into the getHandler function and see what the processing logic is like:
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings ! =null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if(handler ! =null) {
returnhandler; }}}return null;
}
Copy the code
Loop over a Map of a request match and return if a Handler matches successfully
How is the Map initialized and what exactly does it contain?
What exactly is a HandlerExecutionChain?
This part of the question later look, feel is a big project, does not affect the current analysis of this
The request matches the Mapper
Then with bottom go to, to the class: AbstractHandlerMethodMapping. Java
We’ve come to a key processing, and we can see that there’s some matching logic, there’s a lot of processing going on, some of which we don’t know what’s going on right now, but we’ll come back to that later when we look up the data
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
// When will the default Handler be matched?
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// When can I get a Bean that is a String?
ObtainApplicationContext is required
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// When is the cache used?
// Ensure presence of cached lookupPath for interceptors and others
if(! ServletRequestPathUtils.hasCachedPath(request)) { initLookupPath(request); } HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if(logger.isDebugEnabled() && ! DispatcherType.ASYNC.equals(request.getDispatcherType())) { logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if(getCorsConfigurationSource() ! =null) { CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request); config = (globalConfig ! =null ? globalConfig.combine(config) : config);
}
if(config ! =null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
Copy the code
One of them, getHandlerExecutionChain, is questionable and feels like a key code. It sets up a bunch of familiar Interceptors, flagged here and explored later
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if(mappedInterceptor.matches(request)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); }}else{ chain.addInterceptor(interceptor); }}return chain;
}
Copy the code
The Handler matching
Below is followed the breakpoint to: AbstractHandlerMapping. Java
See the key code below: a lock was placed while looking for a match, entering the specific search logic
@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// Here's some processing to get our request path: "/"
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return(handlerMethod ! =null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock(); }}Copy the code
We go into class: AbstractHandlerMethodMapping. In Java, check the specific processing logic
We can see that there’s a bunch of matches going on here, and if it’s null we can take the default value, what’s the default value?
There are also multiple cases where a match does not return an error, so it feels like there are still a lot of tricks in the Web, but we’ll look at these details later and continue to look at the matching logic
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// The Handler is a simple Map value (as shown in the following code). How to initialize the Map should be seen later
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if(directPathMatches ! =null) {
addMatchingMappings(directPathMatches, matches, request);
}
// If there is no match, what happens to the default value?
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
// See some very interesting processing here
// If two matches are made at the same time, it is not necessary to report an error.
if(! matches.isEmpty()) { Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
// What does this return mean?
if (CorsUtils.isPreFlightRequest(request)) {
for (Match match : matches) {
if (match.hasCorsConfig()) {
returnPREFLIGHT_AMBIGUOUS_MATCH; }}}else {
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "' : {" + m1 + "," + m2 + "}");
}
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.getHandlerMethod();
}
else {
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request); }}Copy the code
A simple Map value (as shown in the following code). How does that Map initialize
class MappingRegistry {
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
@Nullable
public List<T> getMappingsByDirectPath(String urlPath) {
return this.pathLookup.get(urlPath); }}Copy the code
At this point, the match is successful and returns, but you can see through debugging that it’s not a familiar class or method
Return (handlerMethod! = null ? handlerMethod.createWithResolvedBean() : null);
Take a look at some of the methods and constructors of class HandlerMethod:
In the following function we see the get Bean for the property
public HandlerMethod createWithResolvedBean(a) {
Object handler = this.bean;
if (this.bean instanceof String) {
Assert.state(this.beanFactory ! =null."Cannot resolve bean name without BeanFactory");
String beanName = (String) this.bean;
handler = this.beanFactory.getBean(beanName);
}
return new HandlerMethod(this, handler);
}
Copy the code
In its constructor, the Bean and Method are initialized directly. We will see how this is initialized later
public HandlerMethod(Object bean, Method method) {
Assert.notNull(bean, "Bean is required");
Assert.notNull(method, "Method is required");
this.bean = bean;
this.beanFactory = null;
this.beanType = ClassUtils.getUserClass(bean);
this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters();
evaluateResponseStatus();
this.description = initDescription(this.beanType, this.method);
}
Copy the code
conclusion
This article explores how specific requests can be matched to handler functions. The core logic is to walk through the following: handlerMappings
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings ! =null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if(handler ! =null) {
returnhandler; }}}return null;
}
Copy the code
There are a lot of details to explore about the specific matching logic behind handlerMappings, but it is more important to clarify this: How is handlerMappings initialized and loaded
We will continue to explore this in the following articles