SpringBoot自动装配原理分析

263 阅读1分钟

  SpringBoot能流行起来我觉得有个重要的原因就是它的自动装配,想想使用SpringBoot与Spring开发项目的区别,第一反应就是减少了很多配置。

  其实SpringBoot是基于Spring基础之上的,通过MAVEN引入SpringBoot也会发现,它传递依赖了Spring。所以配置并没有减少,只是它帮我们自动配置了,这些自动配置的类都在spring-boot-autoconfigure-版本.jar包中。

  以一个最简单的SpringBoot应用为例,通常需要写这样一个启动类:

  进入@SpringBootApplication注解内部,可以看到与自动装配有关的@EnableAutoConfiguration:

  @EnableAutoConfiguration通过@import方式引入了EnableAutoConfigurationImportSelector:

  EnableAutoConfigurationImportSelector是一个ImportSelector,我们查看其selectImports方法,其内部调用getCandidateConfigurations方法,读取META-INF/spring.factories里配置的Configuration类。

  @Override

  public String[] selectImports(AnnotationMetadata annotationMetadata) {

  if (!isEnabled(annotationMetadata)) {

  return NO_IMPORTS;

  }

  try {

  AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);

  AnnotationAttributes attributes = getAttributes(annotationMetadata);

  Listconfigurations = getCandidateConfigurations(annotationMetadata, attributes); // META-INF/spring.factories

  configurations = removeDuplicates(configurations);

  configurations = sort(configurations, autoConfigurationMetadata);

  Setexclusions = getExclusions(annotationMetadata, attributes);

  checkExcludedClasses(configurations, exclusions);

  configurations.removeAll(exclusions);

  configurations = filter(configurations, autoConfigurationMetadata);

  fireAutoConfigurationImportEvents(configurations, exclusions);

  return configurations.toArray(new String[configurations.size()]);

  }

  catch (IOException ex) {

  throw new IllegalStateException(ex);

  }

  }

  在SpringBoot的spring-boot-autoconfigure-版本.jar包中,spring.factories文件里预定义了很多Configuration类:

  # Initializers

  org.springframework.context.ApplicationContextInitializer=\

  org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\

  org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

  # Application Listeners

  org.springframework.context.ApplicationListener=\

  org.springframework.boot.autoconfigure.BackgroundPreinitializer

  # Auto Configuration Import Listeners

  org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\

  org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

  # Auto Configuration Import Filters

  org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\

  org.springframework.boot.autoconfigure.condition.OnClassCondition

  # Auto Configure

  org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

  org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\

  org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\

  org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\

  org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\

  org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

  org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\

  org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\

  org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\

  org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\

  ......

  到这里自动装配的主流程就结束了,然后具体的装配逻辑就在各自的Configuration类中。

  以常见的HttpEncodingAutoConfiguration为例,这些Configuration类都添加了@Configuration注解表名都是配置类,然后通过例如:@ConditionalXXX等判断通过代码方式创建需要用到的Bean。

  @Configuration

  @EnableConfigurationProperties(HttpEncodingProperties.class)

  @ConditionalOnWebApplication

  @ConditionalOnClass(CharacterEncodingFilter.class)

  @ConditionalOnProperty(prefix = spring.http.encoding, value = enabled, matchIfMissing = true)

  public class HttpEncodingAutoConfiguration {

  private final HttpEncodingProperties properties;

  public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {

  this.properties = properties;

  }

  @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;

  }

  @Bean

  public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {

  return new LocaleCharsetMappingsCustomizer(this.properties);

  }

  private static class LocaleCharsetMappingsCustomizer implements EmbeddedServletContainerCustomizer, Ordered {

  private final HttpEncodingProperties properties;

  LocaleCharsetMappingsCustomizer(HttpEncodingProperties properties) {

  this.properties = properties;

  }

  @Override

  public void customize(ConfigurableEmbeddedServletContainer container) {

  if (this.properties.getMapping() != null) {

  container.setLocaleCharsetMappings(this.properties.getMapping());

  }

  }

  @Override

  public int getOrder() {

  return 0;

  }

  }

  }