[SpringMVC源码学习]initHandlerMappings(三)

120 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情

接上一篇分析继续


这时,这一步执行完成,我们返回一个Map集合,此时的泛型 T 就是RequestMappingInfo类型。 然后传回detectHandlerMethods方法里面的lambda函数,就是我们上面所说的函数。

Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
      (MethodIntrospector.MetadataLookup<T>) method -> {
         try {
            return getMappingForMethod(method, userType);
         }
         catch (Throwable ex) {
            throw new IllegalStateException("Invalid mapping on handler class [" +
                  userType.getName() + "]: " + method, ex);
         }
      });

这个Map集合method,他的key是方法method,他的value值就是方法对应的RequestMapping注解所封装好的RequestMappingInfo对象,注意,此时这个RequestMappingInfo对象是已经合并好的。然后进行了一些日志的操作之后,执行到这个地方。

循环刚刚获得到的Map集合。两个方法。

methods.forEach((method, mapping) -> {
   Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
   registerHandlerMethod(handler, invocableMethod, mapping);
});
  • selectInvocableMethod方法

    public static Method selectInvocableMethod(Method method, @Nullable Class<?> targetType) {
       if (targetType == null) {
          return method;
       }
       Method methodToUse = MethodIntrospector.selectInvocableMethod(method, targetType);
       // 不能是私有的,不能是静态的,不能是spring代理的
       if (Modifier.isPrivate(methodToUse.getModifiers()) && !Modifier.isStatic(methodToUse.getModifiers()) &&
             SpringProxy.class.isAssignableFrom(targetType)) {
          throw new IllegalStateException(String.format(
                "Need to invoke method '%s' found on proxy for target class '%s' but cannot " +
                "be delegated to target bean. Switch its visibility to package or protected.",
                method.getName(), method.getDeclaringClass().getSimpleName()));
       }
       return methodToUse;
    }
    

    这个方法其实就是去判断你这个方法,是不是私有的,是不是静态的,是不是spring代理的,如果是,就抛出异常。这里我们也就知道,我们所执行的方法,不能是私有的,静态的或者是spring代理的。筛选完可执行的方法之后,再去执行registerHandlerMethod方法。

  • registerHandlerMethod方法

    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
       this.mappingRegistry.register(mapping, handler, method);
    }
    

    ->

    // mapping是RequestMappingInfo对象,method是处理方法,handler是方法所对应Controller类的类型
    public void register(T mapping, Object handler, Method method) {
       this.readWriteLock.writeLock().lock();
       try {
          // 根据处理类和处理方法创建HandlerMethod类
          // 把方法封装成了HandlerMethod对象
          // 把方法中的参数列表封装成了MethodParameter对象
          HandlerMethod handlerMethod = createHandlerMethod(handler, method);
          validateMethodMapping(handlerMethod, mapping);
    ​
          Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
          for (String path : directPaths) {
             // urlLookup---->(url,RequestMappingInfo)
             this.pathLookup.add(path, mapping);
          }
    ​
          String name = null;
          if (getNamingStrategy() != null) {
             name = getNamingStrategy().getName(handlerMethod, mapping);
             addMappingName(name, handlerMethod);
          }
    ​
          CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
          if (corsConfig != null) {
             corsConfig.validateAllowCredentials();
             this.corsLookup.put(handlerMethod, corsConfig);
          }
    ​
          this.registry.put(mapping,
                new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
       }
       finally {
          this.readWriteLock.writeLock().unlock();
       }
    }
    

    我们直接来看register这个方法,首先就用传过来的处理类和处理方法去创建一个HandlerMethod对象。

    protected HandlerMethod createHandlerMethod(Object handler, Method method) {
            return handler instanceof String ? new HandlerMethod((String)handler, this.obtainApplicationContext().getAutowireCapableBeanFactory(), method) : new HandlerMethod(handler, method);
        }
    ​
    ​
    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 = this.initMethodParameters();
            this.evaluateResponseStatus();
            this.description = initDescription(this.beanType, this.method);
        }
    

    这个类中也有很多的属性,这个方法其实就是去new一个HandlerMethod对象,在他的构造方法中,对这些属性一一赋值,也就是说,一个方法对应一个HandlerMethod对象。值得注意的是,这里的parameters类型为MethodParameter[],就是说HandlerMethod把方法参数列表封装成了一个MethodParameter数组。然后在mappingLookup这个集合中,存储mappinghandlerMethod,也就是我们的RequestMappingInfo和其对应的处理方法。接着它遍历从mapping(RequestMappingInfo)拿到的Urls,在把他们放到urlLookup集合中,url为key,mapping为value。此时我们有两个集合,mappingLookup和urlLookup。

    • mappingLookup--------->(RequestMappingInfo,handlerMethod)

    • urlLookup----------------->(url, RequestMappingInfo)

    也就是说,当我们拿到http请求时候,获取url,根据urlLookup这个集合能得到对应的@RequestMapping注解对应的RequestMappingInfo对象,然后从mappingLookup中根据对应的RequestMappingInfo对象去查找对应的处理方法,这样就能进行处理了。处理完之后,RequestMappingHandlerMapping就真正的创建完成了。


总结

  1. 我们从initStrategies方法进入到this.initHandlerMappings(context);方法中
  2. 在容器中获取不到HandlerMapping实例的时候就会去创建
  3. 因为在DispatcherServlet加载的时候会把它类路径下的DispatcherServlet.properties一起加载放入到属性中,所以直接通过HandlerMapping的全类名可以获取到对应的HandlerMapping类型。
  4. 获取到类型之后就去创建,但由于他是InitializingBean的子类,所以会去调用afterPropertiesSet方法。
  5. 通过一系列的调用之后,会拿到spring容器中的所有beanName去一一筛选类上标注了Controller或者RequestMapping的注解的类。
  6. 筛选完成之后,会解析类和方法上面的RequestMapping注解,并把他们封装成RequestMappingInfo对象,然后进行合并,合并完成之后,放到一个map集合中
  7. 遍历刚刚获得的map集合,将RequestMappingInfo对象和对应处理方法放入到mappingLookup,将RequestMappingInfo中的url和对应RequestMappingInfo对象放入到urlLookup中。此时HandlerMapping真正的创建完成。