springboot之mvc原理(二)-能力支持

2020-11-19 16:27:56 浏览数 (1)

springboot之mvc原理(二)-能力支持

概述

前边一篇文章《springboot之mvc原理(一)-请求处理》我们详细分析了springboot应用接收http请求的处理过程,那么进一步的思考一下,为什么应用启动支持就能处理http请求了呢?在应用启动过程中springboot帮我们做了什么呢?DispatchServlet使用的各种组件哪里来的?本篇文章我们将从原理和源码层面对springboot的web能力支持做一下分析和描述。

原理&源码分析

springboot对于web能力的支撑有两个核心的配置类,分别是DispatcherServletAutoConfiguration和WebMvcAutoConfiguration,接下来我们逐个分析一下。

1

DispatcherServletAutoConfiguration

前一篇我们有讲过,springmvc处理请求的核心是DispatcherServlet,那么从名字上我们大致也能看出DispatcherServletAutoConfiguration是DispatcherServlet的自动配置相关实现,核心的地方也就是在springboot应用启动的时候该类实例化DispatcherServlet到容器中并做相关初始化操作。先看一下类签名:

代码语言:javascript复制
/**
 * {@link EnableAutoConfiguration Auto-configuration} for the Spring
 * {@link DispatcherServlet}. Should work for a standalone application where an embedded
 * web server is already present and also for a deployable application using
 * {@link SpringBootServletInitializer}.
 *
 * @author Phillip Webb
 * @author Dave Syer
 * @author Stephane Nicoll
 * @author Brian Clozel
 */
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
@EnableConfigurationProperties(ServerProperties.class)
public class DispatcherServletAutoConfiguration {
    ···
}

类签名的大致意思是提供DispatcherServlet的自动配置,依赖于拥有内置web服务器的独立的应用来正常工作,类上边有几个注解:

  • @AutoConfigureOrder:自动配置的顺序,此处是最高顺序
  • @Configuration:标记该类是一个配置类,应用启动的时候会解析成配置信息
  • @ConditionalOnWebApplication:以来的web环境,此处以来基于servlet的web环境
  • @ConditionalOnClass:配置以来的类,这里依赖DispatcherServlet(要保证DispatcherServlet被加载)
  • @AutoConfigureAfter:指定该类在哪个类之后配置,此处指定在ServletWebServerFactoryAutoConfiguration之后配置(该类配置应用服务器),后边分析
  • @EnableConfigurationProperties:启用配置信息

前边有提到该配置类要在ServletWebServerFactoryAutoConfiguration之后自动配置,我们看一下ServletWebServerFactoryAutoConfiguration做了什么事情:

代码语言:javascript复制
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
    ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
    ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
    ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

  @Bean
  public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(
      ServerProperties serverProperties) {
    return new ServletWebServerFactoryCustomizer(serverProperties);
  }

  @Bean
  @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
  public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
      ServerProperties serverProperties) {
    return new TomcatServletWebServerFactoryCustomizer(serverProperties);
  }

  /**
   * Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
   * {@link ImportBeanDefinitionRegistrar} for early registration.
   */
  public static class BeanPostProcessorsRegistrar
      implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
      if (beanFactory instanceof ConfigurableListableBeanFactory) {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
      }
    }
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
        BeanDefinitionRegistry registry) {
      if (this.beanFactory == null) {
        return;
      }
      registerSyntheticBeanIfMissing(registry,
          "webServerFactoryCustomizerBeanPostProcessor",
          WebServerFactoryCustomizerBeanPostProcessor.class);
      registerSyntheticBeanIfMissing(registry,
          "errorPageRegistrarBeanPostProcessor",
          ErrorPageRegistrarBeanPostProcessor.class);
    }
    private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
        String name, Class<?> beanClass) {
      if (ObjectUtils.isEmpty(
          this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
        beanDefinition.setSynthetic(true);
        registry.registerBeanDefinition(name, beanDefinition);
      }
    }
  }
}

该类不做深入分析,大致是依赖web环境并且做了以下几件事情:

  • 导入支持的3种内置servlet容器:tomcat,jetty和undertow
  • 导入内部BeanPostProcessorsRegistrar类,用于注册WebServerFactoryCustomizerBeanPostProcessor
  • 初始化一个默认的tomcat容器

简单来说该类提供了web的基础环境相关能力支持。

接着我们看一下处理请求的核心类DispatcherServlet的配置:

代码语言:javascript复制
@Configuration
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {

  private final WebMvcProperties webMvcProperties;

  public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {
    this.webMvcProperties = webMvcProperties;
  }
  @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
  public DispatcherServlet dispatcherServlet() {
    DispatcherServlet dispatcherServlet = new DispatcherServlet();
    dispatcherServlet.setDispatchOptionsRequest(
        this.webMvcProperties.isDispatchOptionsRequest());
    dispatcherServlet.setDispatchTraceRequest(
        this.webMvcProperties.isDispatchTraceRequest());
    dispatcherServlet.setThrowExceptionIfNoHandlerFound(
        this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
    return dispatcherServlet;
  }
  @Bean
  @ConditionalOnBean(MultipartResolver.class)
  @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
  public MultipartResolver multipartResolver(MultipartResolver resolver) {
    // Detect if the user has created a MultipartResolver but named it incorrectly
    return resolver;
  }
}

从配置依赖项和配置内容两个维度分析该配置类:

配置依赖项

  • 依赖DefaultDispatcherServletCondition的match结果,也就是检查容器中是否已经注入了名称为dispatcherServlet的bean
  • 依赖ServletRegistration类,该类主要有容器实现,用于将servlet和其他组件添加到容器中

配置内容

  • 初始化并暴露DispatcherServlet,设置名称为dispatcherServlet并做一些基本属性配置
  • 初始化分段上传处理器MultipartResolver,使用的注解非常有意思,同时用了@ConditionalOnBean和@ConditionalOnMissingBean,目的是保证如果定义了MultipartResolver但是bean的命名不正确,修正为multipartResolver,如果定义了并且命名正确,此处略过

DispatcherServletAutoConfiguration中还有一个配置类DispatcherServletRegistrationConfiguration,从名字大概可以猜得出和Servlet注册相关:

代码语言:javascript复制
@Configuration
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {

  private final ServerProperties serverProperties;

  private final WebMvcProperties webMvcProperties;

  private final MultipartConfigElement multipartConfig;

  public DispatcherServletRegistrationConfiguration(
      ServerProperties serverProperties, WebMvcProperties webMvcProperties,
      ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
    this.serverProperties = serverProperties;
    this.webMvcProperties = webMvcProperties;
    this.multipartConfig = multipartConfigProvider.getIfAvailable();
  }

  @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
  @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
  public DispatcherServletRegistrationBean dispatcherServletRegistration(
      DispatcherServlet dispatcherServlet) {
    DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
        dispatcherServlet, this.serverProperties.getServlet().getPath());
    registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
    registration.setLoadOnStartup(
        this.webMvcProperties.getServlet().getLoadOnStartup());
    if (this.multipartConfig != null) {
      registration.setMultipartConfig(this.multipartConfig);
    }
    return registration;
  }
}

同样,我们也从配置依赖项和配置内容维度分析一下该类:

配置依赖项

  • 依赖DispatcherServletRegistrationCondition的match结果,逻辑是如果容器中有dispatcherServlet并且没有dispatcherServletRegistration(类型是ServletRegistrationBean)返回true,否则返回false
  • 依赖ServletRegistration类,该类主要有容器实现,用于将servlet和其他组件添加到容器中
  • 导入DispatcherServletConfiguration配置,直接使用其定义的dispatcherServlet

配置内容

  • 配置DispatcherServletRegistrationBean注册到容器中,该类的继承关系如下

其实现了ServletContextInitializer接口,在onStartup方法中对应的Servlet注册到Servlet容器中,所以这里DispatcherServlet会被注册到Servlet容器中,对应的urlMapping为server.servletPath配置.默认为/,加载顺序为-1,注册DispatcherServlet的执行时机和时序图如下:

也就是会在springboot启动的时候执行,中间省略了创建容器相关逻辑。

2

WebMvcAutoConfiguration

前一节的DispatcherServletAutoConfiguration配置主要提供了和servlet容器相关的web能力的自动配置,那么接下来讲述的WebMvcAutoConfiguration是springboot提供的对于mvc能力支持的自动配置。

先看一下配置类的基本信息:

代码语言:javascript复制
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE   10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
    ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
    ...
}

从类名基本可以看出来,该配置类提供mvc能力的自动配置,根据注解分析一下依赖关系:

  • @ConditionalOnWebApplication:以来的web环境,此处以来基于servlet的web环境
  • @ConditionalOnClass:依赖于类路径中Servlet,DispatcherServlet和WebMvcConfigurer类
  • @ConditionalOnMissingBean:容器中不能有WebMvcConfigurationSupport类和其子类实例
  • @AutoConfigureOrder:自动配置顺序,设置为最高优先级 10
  • @AutoConfigureAfter:指定在DispatcherServletAutoConfiguration和ValidationAutoConfiguration之后配置,DispatcherServletAutoConfiguration就是前边我们提到的DispatcherServlet自动配置,此处依赖它

WebMvcAutoConfiguration有三个内部配置类,分别是WebMvcAutoConfigurationAdapter,EnableWebMvcConfiguration和ResourceChainCustomizerConfiguration,WebMvcAutoConfigurationAdapter声明在WebMvcAutoConfiguration中,是为了确保当该类不在类路径下时不会被读取到,ResourceChainCustomizerConfiguration主要处理资源相关自定义配置,EnableWebMvcConfiguration提供了启用mvc能力的配置,我们重点分析一下EnableWebMvcConfiguration:

代码语言:javascript复制
/**
 * Configuration equivalent to {@code @EnableWebMvc}.
 */
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {

  private final WebMvcProperties mvcProperties;

  private final ListableBeanFactory beanFactory;

  private final WebMvcRegistrations mvcRegistrations;

  public EnableWebMvcConfiguration(
      ObjectProvider<WebMvcProperties> mvcPropertiesProvider,
      ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider,
      ListableBeanFactory beanFactory) {
    this.mvcProperties = mvcPropertiesProvider.getIfAvailable();
    this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
    this.beanFactory = beanFactory;
  }
  @Bean
  @Override
  public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
    adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null
        || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
    return adapter;
  }
  @Override
  protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
    if (this.mvcRegistrations != null
        && this.mvcRegistrations.getRequestMappingHandlerAdapter() != null) {
      return this.mvcRegistrations.getRequestMappingHandlerAdapter();
    }
    return super.createRequestMappingHandlerAdapter();
  }
  @Bean
  @Primary
  @Override
  public RequestMappingHandlerMapping requestMappingHandlerMapping() {
    // Must be @Primary for MvcUriComponentsBuilder to work
    return super.requestMappingHandlerMapping();
  }
  @Override
  protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
    if (this.mvcRegistrations != null
        && this.mvcRegistrations.getRequestMappingHandlerMapping() != null) {
      return this.mvcRegistrations.getRequestMappingHandlerMapping();
    }
    return super.createRequestMappingHandlerMapping();
  }
  @Bean
  @Override
  public ContentNegotiationManager mvcContentNegotiationManager() {
    ContentNegotiationManager manager = super.mvcContentNegotiationManager();
    List<ContentNegotiationStrategy> strategies = manager.getStrategies();
    ListIterator<ContentNegotiationStrategy> iterator = strategies.listIterator();
    while (iterator.hasNext()) {
      ContentNegotiationStrategy strategy = iterator.next();
      if (strategy instanceof PathExtensionContentNegotiationStrategy) {
        iterator.set(new OptionalPathExtensionContentNegotiationStrategy(
            strategy));
      }
    }
    return manager;
  }
    ···
}
代码语言:javascript复制

该类继承自DelegatingWebMvcConfiguration类,并且提供了开启mvc能力的默认实现,中间省略掉了一些代码,剩下三个比较核心的bean配置:

  • requestMappingHandlerAdapter:配置并返回处理基于注解的控制器方法的处理器,通过覆盖方法可以实现添加自定义参数解析器,自定义返回值处理器和自定义消息转换器
  • requestMappingHandlerMapping:配置并返回请求与基于注解的控制器之间映射关系
  • mvcContentNegotiationManager:配置并返回内容协商管理器,用于确定给定请求中MediaType类型并选择合适的视图解析器处理视图

然后我们看一下EnableWebMvcConfiguration继承关系:

该类依次继承自DelegatingWebMvcConfiguration和WebMvcConfigurationSupport,从DelegatingWebMvcConfiguration源码中可以看出其主要使用了WebMvcConfigurationSupport的默认实现,唯一区别是将系统自带和用户自定义的WebMvcConfigurer包装成一个组合WebMvcConfigurerComposite并返回备用,其用途是早期的springboot版本中通过@EnableWebMvc开启mvc能力:

代码语言:javascript复制
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

从WebMvcConfigurationSupport源码中可以看出它是springboot支持mvc能力的核心定义和配置类,一般通过前边的@EnableWebMvc注解手动开启方式和新版本的EnableWebMvcConfiguration自动开启方式,该类注册了以下几种HandlerMapping:

  • RequestMappingHandlerMapping:用于将请求映射到带注释的控制器方法,顺序为0
  • HandlerMapping:将URL路径直接映射到视图名称,顺序为1
  • BeanNameUrlHandlerMapping:将URL路径映射到控制器bean名称,顺序为2
  • HandlerMapping:处理静态资源请求映射
  • HandlerMapping:将请求转发到默认servlet

有三个同名的HandlerMapping,其旨在交给子类去自定义实现,从HandlerMapping注册的顺序我们也能感受到现在springboot也主要推崇基于注解的请求处理方式,另外也注册了3种HandlerAdapter来处理真实请求:

  • RequestMappingHandlerAdapter:用于使用带注释的控制器方法处理请求
  • HttpRequestHandlerAdapter:用于使用HttpRequestHandler处理请求
  • SimpleControllerHandlerAdapter:用于通过基于接口的Controller处理请求

以及其他异常处理器、内容协商管理器等默认实现和配置。

由于EnableWebMvcConfiguration中RequestMappingHandlerAdapter和RequestMappingHandlerMapping的配置方式比较像,我们就直接分析其中RequestMappingHandlerMapping即可,看一下requestMappingHandlerMapping方法实现:

代码语言:javascript复制
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
  // Must be @Primary for MvcUriComponentsBuilder to work
  return super.requestMappingHandlerMapping();
}

直接调用了父类实现:

代码语言:javascript复制
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
  RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
  mapping.setOrder(0);
  mapping.setInterceptors(getInterceptors());
  mapping.setContentNegotiationManager(mvcContentNegotiationManager());
  mapping.setCorsConfigurations(getCorsConfigurations());

  PathMatchConfigurer configurer = getPathMatchConfigurer();

  Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
  if (useSuffixPatternMatch != null) {
    mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
  }
  Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
  if (useRegisteredSuffixPatternMatch != null) {
    mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
  }
  Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
  if (useTrailingSlashMatch != null) {
    mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
  }

  UrlPathHelper pathHelper = configurer.getUrlPathHelper();
  if (pathHelper != null) {
    mapping.setUrlPathHelper(pathHelper);
  }
  PathMatcher pathMatcher = configurer.getPathMatcher();
  if (pathMatcher != null) {
    mapping.setPathMatcher(pathMatcher);
  }
  return mapping;
}

该方法创建了一个RequestMappingHandlerMapping,设置顺序为0、设置拦截器、配置内容协商管理器、cors配置和路径匹配相关配置等,创建RequestMappingHandlerMapping调用的是受保护的方法createRequestMappingHandlerMapping(子类可覆盖实现自定义),EnableWebMvcConfiguration中对于该方法进行了重写:

代码语言:javascript复制
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
  if (this.mvcRegistrations != null
      && this.mvcRegistrations.getRequestMappingHandlerMapping() != null) {
    return this.mvcRegistrations.getRequestMappingHandlerMapping();
  }
  return super.createRequestMappingHandlerMapping();
}

如果mvcRegistrations中有HandlerMapping直接取出返回,否则还是调用父类的方法并返回结果,对于框架的自动配置基本不会调用有参构造器,所以该方法在没有重写的情况下还是直接调用父类实现。

接着我们看一下RequestMappingHandlerMapping原理和实现,先看一下继承关系:

其本质是一个HandlerMapping并且实现了InitializingBean,createRequestMappingHandlerMapping调用的是无参构造器来创建RequestMappingHandlerMapping实例,那么其初始化动作也就由afterPropertiesSet完成,看一下实现:

代码语言:javascript复制
@Override
public void afterPropertiesSet() {
  this.config = new RequestMappingInfo.BuilderConfiguration();
  this.config.setUrlPathHelper(getUrlPathHelper());
  this.config.setPathMatcher(getPathMatcher());
  this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
  this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
  this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
  this.config.setContentNegotiationManager(getContentNegotiationManager());

  super.afterPropertiesSet();
}

创建了一个配置项容器,并初始化然后调用父类(AbstractHandlerMethodMapping)的afterPropertiesSet实现:

代码语言:javascript复制
/**
 * Detects handler methods at initialization.
 * @see #initHandlerMethods
 */
@Override
public void afterPropertiesSet() {
  initHandlerMethods();
}

该方法调用初始化方法检查处理程序:

代码语言:javascript复制
/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #isHandler
 * @see #detectHandlerMethods
 * @see #handlerMethodsInitialized
 */
protected void initHandlerMethods() {
  if (logger.isDebugEnabled()) {
    logger.debug("Looking for request mappings in application context: "   getApplicationContext());
  }
    //1.获取spring容器中所有的bean实例名称
  String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
      BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
      obtainApplicationContext().getBeanNamesForType(Object.class));
    //2.遍历名称
  for (String beanName : beanNames) {
        //3.跳过代理类的检测
    if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
      Class<?> beanType = null;
      try {
                //4.获取bean的实际类型
        beanType = obtainApplicationContext().getType(beanName);
      }
      catch (Throwable ex) {
        // An unresolvable bean type, probably from a lazy bean - let's ignore it.
        if (logger.isDebugEnabled()) {
          logger.debug("Could not resolve target class for bean with name '"   beanName   "'", ex);
        }
      }
            //5.检查bean的类型是否是handler
      if (beanType != null && isHandler(beanType)) {
                //6.在目标类型中寻找处理方法并注册备用
        detectHandlerMethods(beanName);
      }
    }
  }
    //7.在检测到所有处理程序方法之后调用,是一个钩子方法供子类覆盖实现,默认为空实现
  handlerMethodsInitialized(getHandlerMethods());
}
代码语言:javascript复制
    具体的逻辑在方法中都做了注释,重点看一下第5和6点,先看isHandler实现:
代码语言:javascript复制
@Override
protected boolean isHandler(Class<?> beanType) {
  return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
      AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

检查目标类型上是否有@Controller或@RequestMapping注解,如果有就认为是一个请求处理类。再看一下detectHandlerMethods实现:

代码语言:javascript复制
/**
 * Look for handler methods in the specified handler bean.
 * @param handler either a bean name or an actual handler instance
 * @see #getMappingForMethod
 */
protected void detectHandlerMethods(Object handler) {
    //1.获取handler的实际类型
  Class<?> handlerType = (handler instanceof String ?
      obtainApplicationContext().getType((String) handler) : handler.getClass());

  if (handlerType != null) {
        //2.获取handlerType原始类型,通常是一样的,如果被CGLIB代理则返回原始类型
    Class<?> userType = ClassUtils.getUserClass(handlerType);
        //3.使用方法内省工具类,获取Method与RequestMappingInfo映射关系
    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);
          }
        });
    if (logger.isDebugEnabled()) {
      logger.debug(methods.size()   " request handler methods found on "   userType   ": "   methods);
    }
        //4.遍历映射关系,并注册到MappingRegistry备用
    methods.forEach((method, mapping) -> {
      Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
      registerHandlerMethod(handler, invocableMethod, mapping);
    });
  }
}

方法的核心是注释中的第3和4点,3中使用MethodIntrospector返回Method与RequestMappingInfo之间映射,4是将方法、mapping以及目标类的关系注册到MappingRegistry中。首先看一下selectMethods:

代码语言:javascript复制
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
  final Map<Method, T> methodMap = new LinkedHashMap<>();
  Set<Class<?>> handlerTypes = new LinkedHashSet<>();
  Class<?> specificHandlerType = null;
  //1.检查是否被代理,如果被代理获取原始类并存储备用
  if (!Proxy.isProxyClass(targetType)) {
    specificHandlerType = ClassUtils.getUserClass(targetType);
    handlerTypes.add(specificHandlerType);
  }
    //2.获取目标类的所有实现接口并放入handlerTypes
  handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
  //3.遍历接口集合
  for (Class<?> currentHandlerType : handlerTypes) {
        //原始类
    final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
    //4.在给定类或者父类(或接口)所有匹配方法执行指定回调操作,如果方法没有被MethodFilter排除
        //同名的方法会出现两次,此处就是使用传入的metadataLookup检查方法,并放入映射中
    ReflectionUtils.doWithMethods(currentHandlerType, method -> {
      Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
      T result = metadataLookup.inspect(specificMethod);
      if (result != null) {
        Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
        if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
          methodMap.put(specificMethod, result);
        }
      }
    }, ReflectionUtils.USER_DECLARED_METHODS);
  }

  return methodMap;
}

MethodIntrospector.selectMethods方法使用了典型的委托模式,方法传入一个目标类,和具体处理程序,selectMethods只负责调用实际处理程序处理相应参数并返回结果,实际处理程序是一个MethodIntrospector.MetadataLookup类型的匿名实现,调用AbstractHandlerMethodMapping子类实现的getMappingForMethod方法并返回目标类每个方法的请求映射信息,直接看子类RequestMappingHandlerMapping的实现:

代码语言:javascript复制
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
  RequestMappingInfo info = createRequestMappingInfo(method);
  if (info != null) {
    RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
    if (typeInfo != null) {
      info = typeInfo.combine(info);
    }
  }
  return info;
}

这个方法不再深入展开分析,作用就是根据method和handlerType上的注解信息获取RequestMappingInfo,然后进行组合并返回(比如method和handlerType都有@RequestMapping注解,需要组合处理)。

然后回到前边讲的第4点将Method与映射关系注册到MappingRegistry,首先获取可执行方法,然后调用registerHandlerMethod将Method包装成HttpMethod并注册处理方法唯一映射。

整个RequestMappingHandlerMapping的初始化流程如下:

3

自动配置

springboot自动配置的原理分析不在本篇的讨论范围,我们基于DispatcherServletAutoConfiguration和WebMvcAutoConfiguration对自动配置做一下针对性介绍,在一个springboot应用的门面类都会加上@SpringBootApplication注解,它是一个组合注解,并且上边有@EnableAutoConfiguration注解,看一下其实现:

代码语言:javascript复制
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

  /**
   * Exclude specific auto-configuration classes such that they will never be applied.
   * @return the classes to exclude
   */
  Class<?>[] exclude() default {};

  /**
   * Exclude specific auto-configuration class names such that they will never be
   * applied.
   * @return the class names to exclude
   * @since 1.3.0
   */
  String[] excludeName() default {};
}

上边有两个注解,@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class),前者的作用是对注解作用的类所在包名扫描并将该包名下扫描到的需要注册的bean进行注册,AutoConfigurationImportSelector对SpringFactoriesLoader从"META-INF/spring.factories"中"key=org.springframework.boot.autoconfigure.EnableAutoConfiguration"加载的那一堆使用@Configuration注解的配置类进行解析注册,当然这里边也包含我们前边分析的两个重要的配置类:

这也是springboot自动配置的核心实现和支持。

总结

本篇我们从两个核心配置类作为切入点,来了解springboot对mvc能力的支持,并从原理和源码维度展开了分析和研究,总结起来有3个比较关键的点:

  • DispatcherServletAutoConfiguration:初始化DispatcherServlet并做一些容器和servlet层面的初始化设置和准备
  • WebMvcAutoConfiguration:开启mvc能力并初始化各个处理组件
  • RequestMappingHandlerMapping:初始化请求与处理器method之间的映射关系

当然还有其他组件的初始化操作,比如RequestMappingHandlerAdapter、内容协商管理器和异常处理器等等,此处不再做分析。

0 人点赞