What is the handler in spring MVC

Keywords: Java Spring Back-end

     1. What is a handler
     2. How to get the handler (if you don't pay attention to the analysis process, you can skip and look at the summary directly
     3. Simple summary of handle

1. What is a handler

     Students who have learned about the spigmvc process must have heard of the handler. Baidu translates it as a handler, which is called a processor in many blogs. Then call it a controller according to what most people say. When it comes to the controller, do you think of all kinds of controllers in our business code, which are also controllers? Is it a kind of thing? Here you can boldly guess that it is a kind of thing Well, now verify and guess through the source code!

     If you search directly from the source code according to the class file type, you can't find the handler. According to the workflow of spring MVC (many posts said that it's not repeated here), the handler first appeared in this place:
getHandler() in AbstractHandlerMapping.java

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		// Get the handler. If the get is empty, the default handler will be used. If there is no default handler, null will be returned
		Object handler = getHandlerInternal(request);
		// Omit part of the code
	}

Actual request sent by test case:

@RequestMapping("/test")
@RestController
public class Test {

    @GetMapping("/add")
    public String add(String a,Personal personal,@RequestParam(name = "genderType") int gender){
        int i=0;
        return a;
   		 }
    }

     Take a look at debug corresponding to the request
Object handler = getHandlerInternal(request);
What is the specific content of handler in? The screenshot is as follows

     It can be seen from this that the handler is actually an object of type HandlerMethod. The attributes in it include the class information, request method, request parameters, etc. Therefore, it can be considered that the handler is equivalent to the controller class and method information corresponding to each request in the normal business code. The above debug screenshot is easier to understand!

2. How is the handler obtained

         There will be package scanning during the project startup. The scanning content related to this post is to scan the class information marked with @ RequestMapping on the class and the method information marked with @ RequestMapping on the method into the container (note that the method information marked with @ GetMapping, @ PostMapping and other methods conforming to the restful style will also be scanned, which will be introduced below) . each method with the above annotation will have a requestmappinginfo (request mapping information, which is actually the data assembly information of each attribute and attribute value in the annotation) when scanned into the container.

         The specific request process is actually to obtain the corresponding MappingRegistration from map < RequestMappingInfo, MappingRegistration < RequestMappingInfo > > according to the RequestMappingInfo corresponding to the request, including the handler (actually the HandlerMethod).

         Let's start the project and scan @ RequestMapping for core processing
getMappingForMethod() in RequestMappingHandlerMapping.java

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		// Obtain RequestMappingInfo according to the @ RequestMapping annotation marked on the method 
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
		// Obtain RequestMappingInfo according to the @ RequestMapping annotation marked on the class where the method is located 
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
			// The RequestMappingInfo obtained by the class and the RequestMappingInfo obtained by the method will be assembled to generate the RequestMappingInfo at the new method level 
				info = typeInfo.combine(info);
			}
			// Omit part of the code
		}
		return info;
	}

     Processing of creating RequestMapping based on class information or method information:
createRequestMappingInfo() in RequestMappingHandlerMapping.java

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// Get the RequestMapping annotation information with @ RequestMapping annotation on the class or method (@ getmapping, @ PostMapping, etc. are all parsed according to @ RequestMapping, and such annotation is only @ RequestMapping specifying the method as the corresponding GTE or POST request method)
		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
		// Get the custom request condition on the class or method. null is returned here
		RequestCondition<?> condition = (element instanceof Class ?
				getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
		return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
	}

     Specific assembly of RequestMappingInfo logic:
Overloaded method of createRequestMappingInfo() in RequestMappingHandlerMapping.java

protected RequestMappingInfo createRequestMappingInfo(
			RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
		// Here is to read various attribute values in the @ RequestMapping annotation, such as request path information, request header information, request method information, etc.
		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();
	}

     At this point, the project is started. All the methods with @ RequestMapping annotation in the scanned classes have a RequestMappingInfo created and loaded.
     Send request to get handler method logic
getHandlerInternal in AbstractHandlerMethodMapping.java

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		// Get the request path, which will not be expanded here
		String lookupPath = initLookupPath(request);
		// Omit some codes
		try {
		// Obtain the HandlerMethod according to the request path and request information 
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		// Omit some codes
	}

     The specific logic obtained is
lookupHandlerMethod in AbstractHandlerMethodMapping.java

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
	addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
		}
		if (!matches.isEmpty()) {
			Match bestMatch = matches.get(0);
			// Omit some codes
			bestMatch.getHandlerMethod());
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.getHandlerMethod();
		}
		// Omit some codes
	}

     Sort out the above logic: HandlerMethod is obtained by bestMatch.getHandlerMethod(), bestMatch is obtained by matches.get(0), and matches is encapsulated by addMatchingMappings(directPathMatches, matches, request). Let's continue to look at the addMatchingMappings method
addMatchingMappings() in AbstractHandlerMethodMapping.java

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
		for (T mapping : mappings) {
			T match = getMatchingMapping(mapping, request);
			if (match != null) {
				matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
			}
		}
	}

     From the above, it can be found that matches are assembled by Match. Looking at the Match implementation, it is found that HandlerMethod is derived from getHandlerMethod() (see the Match Implementation). Finally, we can find out the final source of HandlerMethod

private class Match {

		private final T mapping;

		private final MappingRegistration<T> registration;

		public Match(T mapping, MappingRegistration<T> registration) {
			this.mapping = mapping;
			this.registration = registration;
		}

		public HandlerMethod getHandlerMethod() {
			return this.registration.getHandlerMethod();
		}
		// Omit part of the code
	}

     That's the problem. How to assemble the HandlerMethod in MappingRegistration in the mapping registrar. Don't worry, please continue to read
     Through analysis, it is found that during project startup, the container will register and match the method object method in all beans that 'think' is a handler (actually a class with @ Controller or @ RequestMapping, as for why it is described below) with the mapping object information RequestMapping
     isHandler in RequestMappingHandlerMapping.java

protected boolean isHandler(Class<?> beanType) {
	//Defines which beanType conforms to the Handler. That is, there are @ Controller or @ RequestMapping annotations in the class
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

Specific processing of registration matching:
Match the handler with the corresponding requestMapping and put it into the Registrar

public void register(T mapping, Object handler, Method method) {
			
			// Omit part of the code
			// Assemble the HandlerMethod according to the handler and method objects. (the two parameters correspond to the beanName:test of the class in the test case and the request method object: public java.lang.string, com.it.txm.demo.methodreslove.test.add (java.lang.string, com.it.txm.demo.methodreslove.personal, int)); the creation process is a simple constructor creation without in-depth analysis
			HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				
				// Omit part of the code
				// Assemble the requestMappingInfo and the information containing the handlerMethod into the mapping registry
				this.registry.put(mapping,
						new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
			
		}

So far, the context of handlerMethod has been sorted out!

3. Brief summary of handler

          handler is equivalent to the controller class and method information corresponding to each request in ordinary business code. The debug screenshot in the first part of the article is easier to understand!

handler source analysis:
          1. During project startup, create a requestMappingInfo object corresponding to the method with @ RequestMapping annotation (store the attribute information in the annotation);
          2. During the project startup process, the container will match each method in all bean s considered as handlers. During the process, it will create a handlerMethod and assemble the mapping information RequestMappingInfo and mappingregistration (including handlerMethod). The former is key and the latter is value
          3. Send the request, analyze the request information, obtain the MappingRegistration information according to the RequestMappingInfo from map < RequestMappingInfo, MappingRegistration < RequestMappingInfo > >, and obtain the HandlerMethod

Posted by mad3533 on Wed, 24 Nov 2021 16:49:14 -0800