spring boot 源码解析17-mvc自动化配置揭秘

前言

之前的几篇文章,我们介绍到了spring 4 带来的改变–> java 零配置,那么spring boot 是怎么在它的基础上实现自动配置的呢?由于这点设计的范围过大,因此这里我们就只关注mvc方面的自动化配置,涉及如下几个类:

  • EmbeddedServletContainerAutoConfiguration(这个已经在spring boot 源码解析12-servlet容器的建立中分析过了,这里就不分析了)
  • DispatcherServletAutoConfiguration
  • ErrorMvcAutoConfiguration(在spring boot 源码解析14-ImportSelector及默认错误页面中已经有解释过了,这里就不解释了)
  • WebMvcAutoConfiguration(这里的内容比较多,我们下一篇文章进行分析)
  • HttpMessageConvertersAutoConfiguration
  • HttpEncodingAutoConfiguration
  • MultipartAutoConfiguration
  • ServerPropertiesAutoConfiguration
  • WebClientAutoConfiguration

DispatcherServletAutoConfiguration

  1. DispatcherServletAutoConfiguration 在类上声明了如下注解:

    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Configuration
    @ConditionalOnWebApplication
    @ConditionalOnClass(DispatcherServlet.class)
    @AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class)
    1. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) 说明了它在自动化配置有高优先级
    2. @Configuration 是一个配置类,会交由ConfigurationClassPostProcessor进行加载
    3. @ConditionalOnWebApplication 必须在web环境下生效
    4. @ConditionalOnClass(DispatcherServlet.class) 必须在类路径下有DispatcherServlet存在
    5. @AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class) 在EmbeddedServletContainerAutoConfiguration之后进行加载
  2. 由于DispatcherServletAutoConfiguration有2个内部类,DispatcherServletConfiguration,DispatcherServletRegistrationConfiguration.根据加载规则,会优先加载内部类再加载DispatcherServletAutoConfiguration中的定义.接下来我们分别看下其实现:

    1. DispatcherServletConfiguration有如下注解:

      @Configuration
      @Conditional(DefaultDispatcherServletCondition.class)
      @ConditionalOnClass(ServletRegistration.class)
      @EnableConfigurationProperties(WebMvcProperties.class)
      • @Configuration 表明其是一个配置类
      • @Conditional(DefaultDispatcherServletCondition.class) 引入了一个条件类,其判断逻辑如下:

            public ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
        ConditionMessage.Builder message = ConditionMessage
                .forCondition("Default DispatcherServlet");
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        List<String> dispatchServletBeans = Arrays.asList(beanFactory
                .getBeanNamesForType(DispatcherServlet.class, false, false));
        if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
            return ConditionOutcome.noMatch(message.found("dispatcher servlet bean")
                    .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
        }
        if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
            return ConditionOutcome
                    .noMatch(message.found("non dispatcher servlet bean")
                            .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
        }
        if (dispatchServletBeans.isEmpty()) {
            return ConditionOutcome
                    .match(message.didNotFind("dispatcher servlet beans").atAll());
        }
        return ConditionOutcome.match(message
                .found("dispatcher servlet bean", "dispatcher servlet beans")
                .items(Style.QUOTE, dispatchServletBeans)
                .append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
        }
        1. 如果beanFactory 中存在id 为 dispatcherServlet,类型为DispatcherServlet的bean ,返回不匹配.否则进入第2步
        2. 如果beanFactory中包含id为dispatcherServlet的bean,返回不匹配
        3. 如果beanFactory中不包含类型为DispatcherServlet的bean,返回匹配
        4. 其他情况下,返回匹配
      • @ConditionalOnClass(ServletRegistration.class)–> 当前的类路径下存在ServletRegistration.class.
      • @EnableConfigurationProperties(WebMvcProperties.class)–> 导入了一个属性配置类,其WebMvcProperties.class有如下注解:

        @ConfigurationProperties(prefix = "spring.mvc")

        这也就意味着我们在application.properties 中配置spring.mvc前缀的属性,就会自动配置到WebMvcProperties中.

    2. DispatcherServletConfiguration中有如下被@Bean注解的方法

      1. dispatcherServlet,注册dispatcherServlet,id为dispatcherServlet.代码如下:

        @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        public DispatcherServlet dispatcherServlet() {
        // 实例化DispatcherServlet
        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        // 设置是否允许分发Option请求,默认为true
        dispatcherServlet.setDispatchOptionsRequest(
                this.webMvcProperties.isDispatchOptionsRequest());
        // 设置是否允许分发Trace请求,默认为false
        dispatcherServlet.setDispatchTraceRequest(
                this.webMvcProperties.isDispatchTraceRequest());
        // 设置是否没有handeler处理请求时抛出异常,默认为false
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(
                this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
        return dispatcherServlet;
        }
        1. 实例化DispatcherServlet
        2. 设置是否允许分发Option请求,默认为true
        3. 设置是否允许分发Trace请求,默认为false
        4. 设置是否没有handeler处理请求时抛出异常,默认为false
      2. multipartResolver,代码如下:

        @Bean
        @ConditionalOnBean(MultipartResolver.class)
        @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
        public MultipartResolver multipartResolver(MultipartResolver resolver) {
                    return resolver;
        }

        在beanFactory中存在MultipartResolver类型的bean时,但是其id不是multipartResolver,那么此时就会重新注册一个id为multipartResolver的bean。

    3. DispatcherServletRegistrationConfiguration,有如下注解:

      @Configuration
      @Conditional(DispatcherServletRegistrationCondition.class)// 当beanFactory中有DispatcherServlet,并且id为dispatcherServlet
      @ConditionalOnClass(ServletRegistration.class) // ServletRegistration在类路径存在的话
      @EnableConfigurationProperties(WebMvcProperties.class)
      @Import(DispatcherServletConfiguration.class)
      • @Configuration表明其是一个配置类
      • @Conditional(DispatcherServletRegistrationCondition.class) 引入了DispatcherServletRegistrationCondition条件类,其判断逻辑如下:

        @Override
        public ConditionOutcome getMatchOutcome(ConditionContext context,
            AnnotatedTypeMetadata metadata) {
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);
        if (!outcome.isMatch()) {
            return outcome;
        }
        return checkServletRegistration(beanFactory);
        }
        1. 获得beanFactory
        2. 调用checkDefaultDispatcherName,看是否beanFactory中是否包含id 为dispatcherServlet,类型为DispatcherServlet的bean.如果不包含,则直接返回不匹配,否则进行第3步.代码如下:

          private ConditionOutcome checkDefaultDispatcherName(
          ConfigurableListableBeanFactory beanFactory) {
          List<String> servlets = Arrays.asList(beanFactory
              .getBeanNamesForType(DispatcherServlet.class, false, false));
          boolean containsDispatcherBean = beanFactory
              .containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
          if (containsDispatcherBean
              && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
          return ConditionOutcome
                  .noMatch(startMessage().found("non dispatcher servlet")
                          .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
          }
          return ConditionOutcome.match();
          }
          1. 获得DispatcherServlet类型的bean
          2. containsDispatcherBean =beanFactory 中是否包含id为 dispatcherServlet的bean
          3. 如果beanFactory 中包含id 为dispatcherServlet的bean,但是其类型不是DispatcherServlet,则返回不匹配,否则返回匹配
        3. checkServletRegistration 代码如下:

          private ConditionOutcome checkServletRegistration(
          ConfigurableListableBeanFactory beanFactory) {
          ConditionMessage.Builder message = startMessage();
          List<String> registrations = Arrays.asList(beanFactory
              .getBeanNamesForType(ServletRegistrationBean.class, false, false));
          boolean containsDispatcherRegistrationBean = beanFactory
              .containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
          if (registrations.isEmpty()) {
          if (containsDispatcherRegistrationBean) {
              return ConditionOutcome
                      .noMatch(message.found("non servlet registration bean").items(
                              DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
          }
          return ConditionOutcome
                  .match(message.didNotFind("servlet registration bean").atAll());
          }
          if (registrations
              .contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) {
          return ConditionOutcome.noMatch(message.found("servlet registration bean")
                  .items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
          }
          if (containsDispatcherRegistrationBean) {
          return ConditionOutcome
                  .noMatch(message.found("non servlet registration bean").items(
                          DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
          }
          return ConditionOutcome.match(message.found("servlet registration beans")
              .items(Style.QUOTE, registrations).append("and none is named "
                      + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
          }
          1. 获得beanFactory中类型为ServletRegistrationBean的bean name
          2. containsDispatcherRegistrationBean = beanFactory中是否包含id 为dispatcherServletRegistration的bean
          3. 如果beanFactory中不包含ServletRegistrationBean的bean

            1. 包含id为dispatcherServletRegistration的bean,则返回不匹配
            2. 如果不包含id为dispatcherServletRegistration的bean,则返回匹配
          4. 如果在beanFactory中类型为ServletRegistrationBean的bean name 中包含dispatcherServletRegistration则返回不匹配
          5. 如果在beanFactory中类型为ServletRegistrationBean的bean name 中不包含dispatcherServletRegistration,且 beanFactory中包含id 为dispatcherServletRegistration的bean,则返回不匹配
          6. 其他情况(beanFactory中类型为ServletRegistrationBean的bean name 中不包含dispatcherServletRegistration,其不包含id为dispatcherServletRegistration的bean),返回匹配.
      • @EnableConfigurationProperties(WebMvcProperties.class) 可通过spring.mvc 进行配置
      • @Import(DispatcherServletConfiguration.class) 导入了DispatcherServletConfiguration 的配置.
    4. DispatcherServletRegistrationConfiguration 中没有内部类,只有一个被@bean注解的方法,代码如下:

      @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
      @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
      public ServletRegistrationBean dispatcherServletRegistration(
              DispatcherServlet dispatcherServlet) {
                  ServletRegistrationBean registration = new ServletRegistrationBean(
                  dispatcherServlet, this.serverProperties.getServletMapping());
          registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
          registration.setLoadOnStartup(
                  this.webMvcProperties.getServlet().getLoadOnStartup());
          if (this.multipartConfig != null) {
              registration.setMultipartConfig(this.multipartConfig);
          }
          return registration;
      }
      
      1. 当beanfactory中存在id 为dispatcherServlet,类型为DispatcherServlet则会进行注册(id 为 dispatcherServletRegistration,类型为ServletRegistrationBean),否则不进行注册。
      2. 直接使用DispatcherServlet和server配置中的servletPath路径构造ServletRegistrationBean, ServletRegistrationBean实现了ServletContextInitializer接口,在onStartup方法中对应的Servlet注册到Servlet容器中,所以这里DispatcherServlet会被注册到Servlet容器中,对应的urlMapping为server.servletPath配置.默认为/,加载顺序为-1.

HttpMessageConvertersAutoConfiguration

  1. HttpMessageConvertersAutoConfiguration在类上有如下注解:

    @Configuration
    @ConditionalOnClass(HttpMessageConverter.class)
    @AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class })
    @Import({ JacksonHttpMessageConvertersConfiguration.class,
        GsonHttpMessageConvertersConfiguration.class })
    • @Configuration–>配置类
    • @ConditionalOnClass(HttpMessageConverter.class)–>当前类路径下存在HttpMessageConverter
    • @AutoConfigureAfter({ GsonAutoConfiguration.class, JacksonAutoConfiguration.class }) –> 在GsonAutoConfiguration,JacksonAutoConfiguration之后进行加载
    • @Import({ JacksonHttpMessageConvertersConfiguration.class,GsonHttpMessageConvertersConfiguration.class })–>导入了JacksonHttpMessageConvertersConfiguration,GsonHttpMessageConvertersConfiguration的配置
  2. HttpMessageConvertersAutoConfiguration中有StringHttpMessageConverterConfiguration内部类,因此为因此对其进行加载.

    1. StringHttpMessageConverterConfiguration有如下注解:

      @Configuration
      @ConditionalOnClass(StringHttpMessageConverter.class)
      @EnableConfigurationProperties(HttpEncodingProperties.class)
      1. 当前类路径下存在StringHttpMessageConverter
      2. 可以在application.properties中通过spring.http.encoding进行配置
    2. StringHttpMessageConverterConfiguration中声明的bean只有一个,当beanFactory中不存在StringHttpMessageConverter时进行注册,如下:

      @Bean
      @ConditionalOnMissingBean
      public StringHttpMessageConverter stringHttpMessageConverter() {
          StringHttpMessageConverter converter = new StringHttpMessageConverter(
                  this.encodingProperties.getCharset());
          converter.setWriteAcceptCharset(false);
          return converter;
      }

      StringHttpMessageConverter 默认编码为:UTF-8

  3. HttpMessageConvertersAutoConfiguration 只注册了一个HttpMessageConverters,当beanFactory中不存在HttpMessageConverters的bean时.代码如下:

    @Bean
    @ConditionalOnMissingBean
    public HttpMessageConverters messageConverters() {
        return new HttpMessageConverters(this.converters == null
                ? Collections.<HttpMessageConverter<?>>emptyList() : this.converters);
    }

HttpEncodingAutoConfiguration

  1. HttpEncodingAutoConfiguration类上有如下注解:

    @Configuration
    @EnableConfigurationProperties(HttpEncodingProperties.class)
    @ConditionalOnWebApplication
    @ConditionalOnClass(CharacterEncodingFilter.class)
    @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
    • @Configuration –> 配置类
    • @EnableConfigurationProperties(HttpEncodingProperties.class) –> 可通过spring.http.encoding.xxx 进行配置
    • @ConditionalOnWebApplication–>web 环境
    • @ConditionalOnClass(CharacterEncodingFilter.class) –> 当前类路径下存在CharacterEncodingFilter
    • @ConditionalOnProperty(prefix = “spring.http.encoding”, value = “enabled”, matchIfMissing = true) –> spring.http.encoding的值为true时,方可进行配置,默认为true
  2. HttpEncodingAutoConfiguration中不存在内部类,其注册了2个bean,分别如下:

    1. 注册CharacterEncodingFilter,当beanFactory中不存在CharacterEncodingFilter类型的bean时.代码如下;

      @Bean
      @ConditionalOnMissingBean(CharacterEncodingFilter.class)
      public CharacterEncodingFilter characterEncodingFilter() {
      CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
      filter.setEncoding(this.properties.getCharset().name());
      filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
      filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
      return filter;
      }

      声明了CharacterEncodingFilter,默认编码为UTF-8,对请求进行强制编码(默认),对响应编码为utf-8为false(默认,可通过spring.http.encoding.forceResponse = true 进行配置)

    2. 注册LocaleCharsetMappingsCustomizer.无条件.代码如下:

      @Bean
      public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
      return new LocaleCharsetMappingsCustomizer(this.properties);
      }
      

MultipartAutoConfiguration

  1. MultipartAutoConfiguration类上有如下注解:

    @Configuration
    @ConditionalOnClass({ Servlet.class, StandardServletMultipartResolver.class,
        MultipartConfigElement.class })
    @ConditionalOnProperty(prefix = "spring.http.multipart", name = "enabled", matchIfMissing = true)
    @EnableConfigurationProperties(MultipartProperties.class)
    • @Configuration –> 配置类
    • @ConditionalOnClass({ Servlet.class,StandardServletMultipartResolver.class,MultipartConfigElement.class }) –> 当前类路径下存在Servlet,StandardServletMultipartResolver,MultipartConfigElement
    • @ConditionalOnProperty(prefix = “spring.http.multipart”, name = “enabled”, matchIfMissing = true)–> spring.http.multipart.enabled 等于true,进行配置,默认为true
    • @EnableConfigurationProperties(MultipartProperties.class) 可通过spring.http.multipart.xxx 进行配置
  2. MultipartAutoConfiguration中没有配置类,其注册了2个bean,代码如下:

    1. 注册MultipartConfigElement,当beanFactory中不存在MultipartConfigElement时注册.代码如下:

      @Bean
      @ConditionalOnMissingBean
      public MultipartConfigElement multipartConfigElement() {
      return this.multipartProperties.createMultipartConfig();
      }

      调用:

      public MultipartConfigElement createMultipartConfig() {
      MultipartConfigFactory factory = new MultipartConfigFactory();
      if (StringUtils.hasText(this.fileSizeThreshold)) {
          factory.setFileSizeThreshold(this.fileSizeThreshold);
      }
      if (StringUtils.hasText(this.location)) {
          factory.setLocation(this.location);
      }
      if (StringUtils.hasText(this.maxRequestSize)) {
          factory.setMaxRequestSize(this.maxRequestSize);
      }
      if (StringUtils.hasText(this.maxFileSize)) {
          factory.setMaxFileSize(this.maxFileSize);
      }
      return factory.createMultipartConfig();
      }
      1. 文件大小默认为0,也就是不限制
      2. 默认请求大小为 10MB
      3. 最大文件大小为 1MB
    2. 注册 StandardServletMultipartResolver.当beanFactory不存在MultipartResolver时.代码如下:

      @Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
      @ConditionalOnMissingBean(MultipartResolver.class)
      public StandardServletMultipartResolver multipartResolver() {
      StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
      multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
      return multipartResolver;
      }

ServerPropertiesAutoConfiguration

  1. ServerPropertiesAutoConfiguration 有如下注解:

    @Configuration
    @EnableConfigurationProperties
    @ConditionalOnWebApplication
    • @Configuration–> 注解类
    • @EnableConfigurationProperties –> 当beanFactory中不存在org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor时,注册如下2个bean:

      1. id 为org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor,class为ConfigurationPropertiesBindingPostProcessor
      2. id为org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store,class为ConfigurationPropertiesBindingPostProcessor
    • @ConditionalOnWebApplication–>web环境
  2. ServerPropertiesAutoConfiguration中没有配置内部类,只有2个被@bean注解的方法,如下:

    1. 注册ServerProperties,当beanFactory中不存在ServerProperties时.代码如下:

          @Bean
      @ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
      public ServerProperties serverProperties() {
      return new ServerProperties();
      }
    2. 注册DuplicateServerPropertiesDetector,代码如下:

          @Bean
      public DuplicateServerPropertiesDetector duplicateServerPropertiesDetector() {
      return new DuplicateServerPropertiesDetector();
      }

      在内置容器配置时会用到,该bean的作用是为了确保只存在一个ServerProperties bean的定义。代码如下:

          @Override
      public void customize(ConfigurableEmbeddedServletContainer container) {
          // ServerProperties handles customization, this just checks we only have
          // a single bean
          String[] serverPropertiesBeans = this.applicationContext
                  .getBeanNamesForType(ServerProperties.class);
          Assert.state(serverPropertiesBeans.length == 1,
                  "Multiple ServerProperties beans registered " + StringUtils
                          .arrayToCommaDelimitedString(serverPropertiesBeans));
      }
      

WebClientAutoConfiguration

  1. WebClientAutoConfiguration 有如下注解:

    @Configuration
    @AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class)
    • @Configuration–> 配置类
    • @AutoConfigureAfter(HttpMessageConvertersAutoConfiguration.class) –> HttpMessageConvertersAutoConfiguration 之后进行加载
  2. WebClientAutoConfiguration中只有一个内部类,RestTemplateConfiguration

    1. RestTemplateConfiguration 注解如下:

      @Configuration
      @ConditionalOnClass(RestTemplate.class)
      • @Configuration –> 配置类
      • @ConditionalOnClass(RestTemplate.class)–> 当前类路径下存在RestTemplate
    2. RestTemplateConfiguration只注册了一个RestTemplateBuilder.当beanFactory中不存在RestTemplateBuilder时.代码如下:

      @Bean
      @ConditionalOnMissingBean
      public RestTemplateBuilder restTemplateBuilder() {
          RestTemplateBuilder builder = new RestTemplateBuilder();
          HttpMessageConverters converters = this.messageConverters.getIfUnique();
          if (converters != null) {
              builder = builder.messageConverters(converters.getConverters());
          }
          List<RestTemplateCustomizer> customizers = this.restTemplateCustomizers
                  .getIfAvailable();
          if (!CollectionUtils.isEmpty(customizers)) {
              customizers = new ArrayList<RestTemplateCustomizer>(customizers);
              AnnotationAwareOrderComparator.sort(customizers);
              builder = builder.customizers(customizers);
          }
          return builder;
      }
      
      1. 实例化RestTemplateBuilder
      2. 如果存在HttpMessageConverters,则进行配置
      3. 如果存在RestTemplateCustomizer,则排序后,进行配置

自动化配置加载流程解析

spring boot 声明了这么多的自动化配置类,那么它的加载流程是怎么的,我们就拿DispatcherServletConfiguration来分析,其它的配置类,加载流程都是一样的.

  1. DispatcherServletAutoConfiguration加载顺序:

    @SpringBootApplication有@EnableAutoConfiguration注解,又因为@Import(EnableAutoConfigurationImportSelector.class),因此在ConfigurationClassParser#doProcessConfigurationClass中会调用processImports 进行处理,最终调用EnableAutoConfigurationImportSelector#selectImports进行处理获得org.springframework.boot.autoconfigure.EnableAutoConfiguration 的配置,因为EnableAutoConfigurationImportSelector是DeferredImportSelector的子类,因此会添加到deferredImportSelectors中,最终在ConfigurationClassParser#processDeferredImportSelectors进行处理.最终调用processImports 进行对org.springframework.boot.autoconfigure.EnableAutoConfiguration配置的类依次进行加载,此时就会对DispatcherServletAutoConfiguration进行加载。

    DispatcherServletAutoConfiguration 不是ImportSelector,ImportBeanDefinitionRegistrar 的子类,因此会当做一个配置类调用processConfigurationClass进行加载.(注意此时DispatcherServletAutoConfiguration是由该应用所导入的) 由于其有内部类,因此会首先加载DispatcherServletConfiguration和DispatcherServletRegistrationConfiguration.

  2. DispatcherServletConfiguration因为有@EnableConfigurationProperties 注解,在@EnableConfigurationProperties注解上有@Import,因此会调用ConfigurationClassParser#processImports.

    1. 在该方法中会调用EnableConfigurationPropertiesImportSelector#selectImports方法,最终返回的是org.springframework.boot.context.properties.EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesBeanRegistrar,org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessorRegistrar.代码如下:

      return new String[] { ConfigurationPropertiesBeanRegistrar.class.getName(),
          ConfigurationPropertiesBindingPostProcessorRegistrar.class.getName() };
    2. ConfigurationPropertiesBeanRegistrar,ConfigurationPropertiesBindingPostProcessorRegistrar 是ImportBeanDefinitionRegistrar的子类,因此会实例化加入到DispatcherServletConfiguration中的ImportBeanDefinitionRegistrar.代码如下:

      configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());

      处理完@Import后,依次处理被@bean注解的方法(dispatcherServlet,multipartResolver),此时向configClass加入了BeanMethod.代码如下:

      Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
      for (MethodMetadata methodMetadata : beanMethods) {
          configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
      }
    3. DispatcherServletConfiguration因为有@Import(DispatcherServletConfiguration.class)注解,因此也就间接导入了DispatcherServletConfiguration,EnableConfigurationPropertiesImportSelector.

      因此加载时会先调用ConfigurationClassParser#processImports.在该方法中会依次遍历DispatcherServletConfiguration,EnableConfigurationPropertiesImportSelector,对于EnableConfigurationPropertiesImportSelector的处理,和DispatcherServletConfiguration一样,这里就不在赘述了.

      对于DispatcherServletConfiguration的处理,会调用processConfigurationClass(candidate.asConfigClass(configClass)),注意此时candidate指的是DispatcherServletConfiguration, configClass指的是DispatcherServletRegistrationConfiguration。

      asConfigClass代码如下:

      public ConfigurationClass asConfigClass(ConfigurationClass importedBy) throws IOException {
          if (this.source instanceof Class) {
              return new ConfigurationClass((Class<?>) this.source, importedBy);
          }
          return new ConfigurationClass((MetadataReader) this.source, importedBy);
      }

      调用

      public ConfigurationClass(MetadataReader metadataReader, ConfigurationClass importedBy) {
      this.metadata = metadataReader.getAnnotationMetadata();
      this.resource = metadataReader.getResource();
      this.importedBy.add(importedBy);
      }

      经过这步处理,意味着DispatcherServletConfiguration所对应的ConfigurationClass是被DispatcherServletRegistrationConfiguration导入的.

      在ConfigurationClassParser#processConfigurationClass中,首先从configurationClasses中查看DispatcherServletConfiguration是否解析过,这里由于DispatcherServletConfiguration已经解析过了,同时DispatcherServletConfiguration是被DispatcherServletRegistrationConfiguration导入的,因此会调用ConfigurationClass#mergeImportedBy.这里的代码如下:

      ConfigurationClass existingClass = this.configurationClasses.get(configClass);
      if (existingClass != null) {
          if (configClass.isImported()) {
              if (existingClass.isImported()) {
                  existingClass.mergeImportedBy(configClass);
              }
              // Otherwise ignore new imported config class; existing non-imported class overrides it.
              return;
          }
          else {
              // Explicit bean definition found, probably replacing an import.
              // Let's remove the old one and go with the new one.
              this.configurationClasses.remove(configClass);
              for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext();) {
                  if (configClass.equals(it.next())) {
                      it.remove();
                  }
              }
          }
      }

      ConfigurationClass#mergeImportedBy 代码如下:

          public void mergeImportedBy(ConfigurationClass otherConfigClass) {
      this.importedBy.addAll(otherConfigClass.importedBy);
      }

      注意,这里加入的是DispatcherServletRegistrationConfiguration

      处理完@Import后,处理被@bean注解的方法–>dispatcherServletRegistration。和DispatcherServletConfiguration一样.

    4. 由于DispatcherServletAutoConfiguration没有被@bean注解的方法,因此DispatcherServletAutoConfiguration的解析到此结束

      因此DispatcherServletAutoConfiguration共解析了3个配置类:

      1. DispatcherServletConfiguration
      2. DispatcherServletRegistrationConfiguration
      3. DispatcherServletAutoConfiguration

      接下来会调用ConfigurationClassBeanDefinitionReader#loadBeanDefinitions进行处理

    5. ConfigurationClassBeanDefinitionReader#loadBeanDefinitions.在该方法中会依次遍历之前解析的ConfigurationClass,调用loadBeanDefinitionsForConfigurationClass.代码如下:

      private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
          TrackedConditionEvaluator trackedConditionEvaluator) {
      
      if (trackedConditionEvaluator.shouldSkip(configClass)) {
          String beanName = configClass.getBeanName();
          if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
              this.registry.removeBeanDefinition(beanName);
          }
          this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
          return;
      }
      
      if (configClass.isImported()) {
          registerBeanDefinitionForImportedConfigurationClass(configClass);
      }
      for (BeanMethod beanMethod : configClass.getBeanMethods()) {
          loadBeanDefinitionsForBeanMethod(beanMethod);
      }
      loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
      loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
      }

      该方法的逻辑如下:

      1. 判断是否需要跳过.
      2. 如果该配置是被导入的,则调用registerBeanDefinitionForImportedConfigurationClass.判断逻辑如下:

        public boolean isImported() {
        return !this.importedBy.isEmpty();
        }

        registerBeanDefinitionForImportedConfigurationClass代码如下:

        private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
        AnnotationMetadata metadata = configClass.getMetadata();
        AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
        ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
        configBeanDef.setScope(scopeMetadata.getScopeName());
        String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
        AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
        configClass.setBeanName(configBeanName);
        if (logger.isDebugEnabled()) {
        logger.debug("Registered bean definition for imported class '" + configBeanName + "'");
        }
        }
        1. 根据configClass中配置的AnnotationMetadata 实例化AnnotatedGenericBeanDefinition
        2. 进行属性的设置

          1. 解析该configClass的Scope,一般是singleton
          2. 生成bean的id
          3. 设置bean的一些属性,如LazyInit,Primary等
        3. 生成BeanDefinitionHolder,并对其尝试进行代理,最后向registry进行注册
      3. 遍历该配置类声明的BeanMethods,调用loadBeanDefinitionsForBeanMethod.

      4. 调用loadBeanDefinitionsFromImportedResources 处理该类导入的配置文件
      5. 调用loadBeanDefinitionsFromRegistrars处理该类配置的importBeanDefinitionRegistrars

      关于这部分的详解,可以看spring boot 源码解析11-ConfigurationClassPostProcessor类加载解析

      1. 对于DispatcherServletConfiguration来说,由于该类是被DispatcherServletAutoConfiguration和DispatcherServletRegistrationConfiguration到入的,因此会调用registerBeanDefinitionForImportedConfigurationClass.因为DispatcherServletConfiguration有ConfigurationPropertiesBeanRegistrar,ConfigurationPropertiesBindingPostProcessorRegistrar,因此会调用loadBeanDefinitionsFromRegistrars.代码如下:

        private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
        for (Map.Entry<ImportBeanDefinitionRegistrar, AnnotationMetadata> entry : registrars.entrySet()) {
            entry.getKey().registerBeanDefinitions(entry.getValue(), this.registry);
        }
        }

        因此会依次调用ConfigurationPropertiesBeanRegistrar,ConfigurationPropertiesBindingPostProcessorRegistrar的registerBeanDefinitions.

        1. ConfigurationPropertiesBeanRegistrar#registerBeanDefinitions,代码如下:

              public void registerBeanDefinitions(AnnotationMetadata metadata,
          BeanDefinitionRegistry registry) {
          MultiValueMap<String, Object> attributes = metadata
              .getAllAnnotationAttributes(
                      EnableConfigurationProperties.class.getName(), false);
          List<Class<?>> types = collectClasses(attributes.get("value"));
          for (Class<?> type : types) {
          String prefix = extractPrefix(type);
          String name = (StringUtils.hasText(prefix) ? prefix + "-" + type.getName()
                  : type.getName());
          if (!registry.containsBeanDefinition(name)) {
              registerBeanDefinition(registry, type, name);
          }
          }
          }

          最终注册了一个id为spring.mvc.org.springframework.boot.autoconfigure.web.WebMvcProperties,类型为org.springframework.boot.autoconfigure.web.WebMvcProperties的bean.

        2. ConfigurationPropertiesBindingPostProcessorRegistrar#registerBeanDefinitions,代码如下:

          @Override
          public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
          BeanDefinitionRegistry registry) {
          if (!registry.containsBeanDefinition(BINDER_BEAN_NAME)) {
          BeanDefinitionBuilder meta = BeanDefinitionBuilder
              .genericBeanDefinition(ConfigurationBeanFactoryMetaData.class);
          BeanDefinitionBuilder bean = BeanDefinitionBuilder.genericBeanDefinition(
              ConfigurationPropertiesBindingPostProcessor.class);
          bean.addPropertyReference("beanMetaDataStore", METADATA_BEAN_NAME);
          registry.registerBeanDefinition(BINDER_BEAN_NAME, bean.getBeanDefinition());
          registry.registerBeanDefinition(METADATA_BEAN_NAME, meta.getBeanDefinition());
          }
          }

          当beanFactory中不存在org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor时,注册如下2个bean:

          1. id 为org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor,class为ConfigurationPropertiesBindingPostProcessor
          2. id为org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store,class为ConfigurationPropertiesBindingPostProcessor
      2. 对于DispatcherServletRegistrationConfiguration来说,由于该类是被DispatcherServletAutoConfiguration导入的,因此会调用registerBeanDefinitionForImportedConfigurationClass.同样,也会调用loadBeanDefinitionsFromRegistrars.在 ConfigurationPropertiesBeanRegistrar#registerBeanDefinitions中,由于之前已经注册了id为spring.mvc.org.springframework.boot.autoconfigure.web.WebMvcProperties,类型为org.springframework.boot.autoconfigure.web.WebMvcProperties的bean.因此可以任何loadBeanDefinitionsFromRegistrars没有产生任何副作用.

      3. 对于 DispatcherServletAutoConfiguration来说,由于是被spring boot的启动类导入的,因此会调用registerBeanDefinitionForImportedConfigurationClass.又因为该类没有被@bean 注解的方法,没有导入其他的配置,因此后续不再处理.

自动配置总结

配置的bean声明的类条件
DispatcherServlet(id为DispatcherServlet)DispatcherServletConfiguration1.web环境 2.beanFactory不存在dispatcherServlet
MultipartResolver(id为multipartResolver)DispatcherServletConfiguration1.web环境,2.beanFactory不存在dispatcherServlet,3 不存在id为multipartResolver,类型为MultipartResolver的bean
dispatcherServletRegistrationDispatcherServletRegistrationConfiguration1.web环境,2.beanFactory中有DispatcherServlet类型,并且id为dispatcherServlet的bean
StringHttpMessageConverter(id为stringHttpMessageConverter)StringHttpMessageConverterConfiguration1.当前类路径下存在StringHttpMessageConverter,2.beanFactory中不存在StringHttpMessageConverter
HttpMessageConverters(id为messageConverters)HttpMessageConvertersAutoConfiguration1.当前类路径下存在StringHttpMessageConverter,2.beanFactory中不存在HttpMessageConverters
CharacterEncodingFilter(id为characterEncodingFilter)HttpEncodingAutoConfiguration1.web 环境,2.当前类路径下存在CharacterEncodingFilter, 3. beanFactory中不存在CharacterEncodingFilter类型的bean
LocaleCharsetMappingsCustomizer(id为localeCharsetMappingsCustomizer)HttpEncodingAutoConfiguration
MultipartConfigElement(id为multipartConfigElement)MultipartAutoConfiguration1.当前类路径下存在Servlet,StandardServletMultipartResolver,MultipartConfigElement,2.spring.http.multipart.enabled 等于true,3.beanFactory中不存在MultipartConfigElement
StandardServletMultipartResolver(id为multipartResolver)MultipartAutoConfiguration1.当前类路径下存在Servlet,StandardServletMultipartResolver,MultipartConfigElement,2.spring.http.multipart.enabled 等于true,3.beanFactory中不存在MultipartResolver
ServerProperties(id为serverProperties)ServerPropertiesAutoConfiguration1.web环境,2.beanFactory中不存在ServerProperties
DuplicateServerPropertiesDetector(id为duplicateServerPropertiesDetector)ServerPropertiesAutoConfiguration1.web环境
RestTemplateBuilder(id为restTemplateBuilder)RestTemplateConfiguration1.当前类路径下存在RestTemplate,2.beanFactory中不存在RestTemplateBuilder
    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/qq_26000415/article/details/78996719
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞