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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

前言

之前的几篇文章,我们介绍到了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 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 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 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.>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 customizers = this.restTemplateCustomizers
                      .getIfAvailable();
              if (!CollectionUtils.isEmpty(customizers)) {
                  customizers = new ArrayList(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 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 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 registrars) {
            for (Map.Entry entry : registrars.entrySet()) {
                entry.getKey().registerBeanDefinitions(entry.getValue(), this.registry);
            }
            }

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

        1. ConfigurationPropertiesBeanRegistrar#registerBeanDefinitions,代码如下:

                  public void registerBeanDefinitions(AnnotationMetadata metadata,
              BeanDefinitionRegistry registry) {
              MultiValueMap attributes = metadata
                  .getAllAnnotationAttributes(
                          EnableConfigurationProperties.class.getName(), false);
              List> 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) DispatcherServletConfiguration 1.web环境 2.beanFactory不存在dispatcherServlet
MultipartResolver(id为multipartResolver) DispatcherServletConfiguration 1.web环境,2.beanFactory不存在dispatcherServlet,3 不存在id为multipartResolver,类型为MultipartResolver的bean
dispatcherServletRegistration DispatcherServletRegistrationConfiguration 1.web环境,2.beanFactory中有DispatcherServlet类型,并且id为dispatcherServlet的bean
StringHttpMessageConverter(id为stringHttpMessageConverter) StringHttpMessageConverterConfiguration 1.当前类路径下存在StringHttpMessageConverter,2.beanFactory中不存在StringHttpMessageConverter
HttpMessageConverters(id为messageConverters) HttpMessageConvertersAutoConfiguration 1.当前类路径下存在StringHttpMessageConverter,2.beanFactory中不存在HttpMessageConverters
CharacterEncodingFilter(id为characterEncodingFilter) HttpEncodingAutoConfiguration 1.web 环境,2.当前类路径下存在CharacterEncodingFilter, 3. beanFactory中不存在CharacterEncodingFilter类型的bean
LocaleCharsetMappingsCustomizer(id为localeCharsetMappingsCustomizer) HttpEncodingAutoConfiguration
MultipartConfigElement(id为multipartConfigElement) MultipartAutoConfiguration 1.当前类路径下存在Servlet,StandardServletMultipartResolver,MultipartConfigElement,2.spring.http.multipart.enabled 等于true,3.beanFactory中不存在MultipartConfigElement
StandardServletMultipartResolver(id为multipartResolver) MultipartAutoConfiguration 1.当前类路径下存在Servlet,StandardServletMultipartResolver,MultipartConfigElement,2.spring.http.multipart.enabled 等于true,3.beanFactory中不存在MultipartResolver
ServerProperties(id为serverProperties) ServerPropertiesAutoConfiguration 1.web环境,2.beanFactory中不存在ServerProperties
DuplicateServerPropertiesDetector(id为duplicateServerPropertiesDetector) ServerPropertiesAutoConfiguration 1.web环境
RestTemplateBuilder(id为restTemplateBuilder) RestTemplateConfiguration 1.当前类路径下存在RestTemplate,2.beanFactory中不存在RestTemplateBuilder

来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring boot 源码解析17-mvc自动化配置揭秘

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏