preface

You’ve already seen the initStrategies() method in DispatchServlet that initializes the HandlerMapping component, so let’s start there!! RequestMappingHandlerMapping, for example.

Step 1: Spring creates the Bean instance of HandlerMapping

For a brief overview, during Spring’s Bean instance creation process, if the InitializingBean interface is implemented before the final Bean, the afterPropertiesSet() method is called.

/ / RequestMappingHandlerMapping overrides the superclass afterPropertiesSet method. Before completing the Bean,
// afterPropertiesSet() is called
@Override
public void afterPropertiesSet(a) {
   // Call the afterPropertiesSet() method of the parent classsuper.afterPropertiesSet();
}

// 
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    public void afterPropertiesSet(a) {(2) initHandlerMethods (); }protected void initHandlerMethods(a) {
       for (String beanName : getCandidateBeanNames()) {
          if(! beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {// Handle the bean in the container, how to handle ????(3) processCandidateBean (beanName); } } handlerMethodsInitialized(getHandlerMethods()); }// Handle the specific logic of the bean in the container
    protected void processCandidateBean(String beanName) { Class<? > beanType =null;
       try {
          beanType = obtainApplicationContext().getType(beanName);
       }
       // isHandler() : checks whether the bean has @controller or @requestMapping annotations
       if(beanType ! =null && isHandler(beanType)) {
          // Monitor the annotated bean(4) detectHandlerMethods (beanName); }}// 
    protected void detectHandlerMethods(Object handler) { Class<? > handlerType = (handlerinstanceof String ?
             obtainApplicationContext().getType((String) handler) : handler.getClass());

       if(handlerType ! =null) { Class<? > userType = ClassUtils.getUserClass(handlerType);/** Get all the annotations in the current Controller@RequestMappingThe Method table Map < Method, RequestMappingInfo >. Eg: the key = com. Shang. Controller. HelloController. Hello () value = {RequestMappingInfo} * /
          Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> {
                   try{5.return getMappingForMethod(method, userType);
                   }
                   catch (Throwable ex) {
                   }
                });
          //        
          methods.forEach((method, mapping) -> {
             Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
             // Add to MappingRegistry6 registerHandlerMethod (handler, invocableMethod, mapping); }); }}}Copy the code

The purpose of the first step is to get the Controller class annotated with @Controller or @requestMapping and then parse all the methods in the Controller to extract the @requestMapping annotated methods. Get a Map < method, requestMappingInfo > collection; Finally, add the Map collection traversal toMappingRegistry, the MappingRegistry is the final answer we need.

Step 2: Get all Bean instances that implement the HandlerMapping interface

private List<HandlerMapping> handlerMappings; // Store all HandlerMapping
private void initHandlerMappings(ApplicationContext context) {
   DetectAllHandlerMappings The default value is true, which can be set manually by programmers
   if (this.detectAllHandlerMappings) {
      // Find all implementation classes that implement the HandlerMapping interface in all containers (including parent containers).
      Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true.false);
      if(! matchingBeans.isEmpty()) {// Assign sort
         this.handlerMappings = new ArrayList<>(matchingBeans.values());
         AnnotationAwareOrderComparator.sort(this.handlerMappings); }}else {
      try {
         // Get the beanName="handlerMapping" Bean instance from the container and assign it to handlerMappings
         HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
         this.handlerMappings = Collections.singletonList(hm); }}/ / if the developer has no configuration HandlerMapping, then from the DispatcherServlet. The properties file
   // Load the default HandlerMapping.
   if (this.handlerMappings == null) {
      // There are many useful utility classes in this method, you can take a look
      this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); }}Copy the code

End goal: Get the implementation class of HandlerMapping in the container (including all parent containers) and add it toList<HandlerMapping>For use.

Step 3: Use the Bean instance of HandlerMapping

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;
}
// AbstractHandlerMapping.java
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   // Get the most appropriate HandlerMethod according to request.
   Object handler = getHandlerInternal(request);
   // Obtain all matched interceptors according to the request and form a HandlerExecutionChain with the Handler Method
   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
   return executionChain;
}

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   // New HandlerExecutionChain object, usually a native handler
   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

One purpose: to get the most appropriate HandlerMethod.

@RequestMapping(value = "/hello", method = {RequestMethod.POST})
public String hello3(a) {
    return "hello";
}
@RequestMapping(value = "/hello", method = {RequestMethod.GET})
public String hello4(a) {
    return "hello";
}
Copy the code

The size of ArrayList is 2 in pathLookup and nameLookup. What happens when the browser requests /hello? The best fit is determined by RequestMappingInfo. For example, compare value first and then method… Of course, the RequestMappingInfo parameters are more than just these two.

Purpose two: Get the HandlerExecutionChain

Encapsulate the Handler Method and HandlerInterceptor as HandlerExecutionChain for use by DispatchServlet.

conclusion