One, foreword

Most of the configuration can be replaced with Java classes + annotations, and the most common one seen in SpringBoot projects is the @SpringBootApplication annotation, which is annotated on every SpringBoot startup class.

What impact does this annotation have on SpringBoot startup and auto-configuration? This article will parse its source code for you, and uncover the mystery of @SpringBootApplication annotation.

Second, the body

Automatic configuration of SpringBoot project is very interested, so learn its source code and sort out some of the content, if there is an error please correct ~ words not to say, directly on the source code;

The source of the @SpringBootApplication annotation is as follows:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
Copy the code

As you can see, this is a compound annotation, including 7 different annotations. The following 7 different annotations are analyzed.

2.1 annotations

Note 1: @target ({elementtype.type})

Used to indicate annotation scope, TYPE means scope is class or interface.

Note 2: @Retention(retentionpolicy.runtime)

Note 3: @documented

Indicates that the comment is logged by Javadoc.

2.1.4 Annotation 4: @Inherited

When the parent class adds the @SpringBootApplication annotation, the subclass inherits the annotation (not applicable to the implementation class of the interface).

2.1.5 Note 5: @springBootConfiguration

The @configuration annotation is still at the bottom, the source code is as follows:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
Copy the code

2.1.6 Note 6: @ComponetScan

The @ComponentScan annotation is very important in Spring. The @ComponentScan annotation corresponds to the @ComponentScan element in the XML configuration, which automatically scans and loads qualified components (@Component, @Repository, etc.) or bean definitions. These bean definitions are eventually loaded into the IoC container.

You can fine-grained customize the scope that @ComponentScan automatically scans through properties such as basePackages. If not specified, the default Spring framework implementation scans from the package of the class in which @ComponentScan is declared. So it is best to place the SpringBoot boot class under the root package, since basePackages are not specified by default.

2.2 Note: @enableAutoConfiguration

In my opinion, the Annotation @enableAutoConfiguration is the most important. Its function can be summarized as: with the help of @import, load all bean definitions that meet the automatic configuration conditions into the IoC container.

The source code is as follows:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<? >[] exclude()default {};
  
  String[] excludeName() default {};
}
Copy the code

Attention is needed here @ AutoConfigurationPackage and @ Import two annotations (AutoConfigurationImportSelector. Class).

2.2.1 Comments: @autoConfigurationPackage

The source code is as follows:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
Copy the code

Can be found that the core of this annotation is the Import annotations, said the annotation for the labeling of class package, should use AutoConfigurationPackages registration. Then look at the Registrar class:

static class Registrar implements ImportBeanDefinitionRegistrar.DeterminableImports {
​
    @Override
      // Metadata is the meta information where our annotations are located
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            // Register all components in our annotations package
      register(registry, new PackageImport(metadata).getPackageName());
    }
​
    @Override
    public Set<Object> determineImports(AnnotationMetadata metadata) {
      return Collections.singleton(newPackageImport(metadata)); }}Copy the code

The core method in this class is the Register method:

private static final String BEAN = AutoConfigurationPackages.class.getName();
  
  public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    if (registry.containsBeanDefinition(BEAN)) {
      BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
      ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
      constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
​    }
    else {
      GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
      beanDefinition.setBeanClass(BasePackages.class);
      beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(BEAN, beanDefinition); }}Copy the code

The logic of the register method is clear: if the bean is already registered, get its constructor parameter value and add the package name to it; Otherwise, create a new bean definition and register it. With the @AutoConfigurationPackage annotation, you can register all components in the annotation package.

2.2.2 comments:@Import(AutoConfigurationImportSelector.class)

This annotation imported AutoConfigurationImportSelector this class of this class core method is a method that selectImports ImportSelector interface. Methods are imported based on the JAR packages and components we configured in the POM.xml file. So the method returns a Class full path array of strings, and the returned Class is managed by the Spring container. The method source code is as follows:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
  if(! isEnabled(annotationMetadata)) {return NO_IMPORTS;
  }
  AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
      .loadMetadata(this.beanClassLoader);
  AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
      annotationMetadata);
  return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
Copy the code

Structure of this method is also very clear, through isEnabled method determine whether need to import in the first place, if you need to import the obtained through loadMetadata configuration information, and through the getAutoConfigurationEntry automatic assembly. The isEnabled method is as follows:

protected boolean isEnabled(AnnotationMetadata metadata) {
  if (getClass() == AutoConfigurationImportSelector.class) {
    return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
  }
  return true;
}
Copy the code

This method by EnableAutoConfiguration. ENABLED_OVERRIDE_PROPERTY determine whether need to automatically configure the configuration items, the default is true. LoadMetadata method source:

protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties";
​
  public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
    return loadMetadata(classLoader, PATH);
  }
​
  static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
    try{ Enumeration<URL> urls = (classLoader ! =null)? classLoader.getResources(path) : ClassLoader.getSystemResources(path); Properties properties =new Properties();
      while (urls.hasMoreElements()) {
        properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
      }
      return loadMetadata(properties);
    }
    catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex); }}static AutoConfigurationMetadata loadMetadata(Properties properties) {
    return new PropertiesAutoConfigurationMetadata(properties);
  }
Copy the code

Can see this method loads the meta-inf/spring – autoconfigure – metadata. The properties of all the configuration information and packaged into AutoConfigurationMetadata object to return.

Note: spring-autoconfigure-metadata.properties file is under spring-boot-autoconfigure-2.1.9.release. jar/ meta-INF.

GetAutoConfigurationEntry method source code is as follows:

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
    if(! isEnabled(annotationMetadata)) {return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = filter(configurations, autoConfigurationMetadata);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}
Copy the code

This method is the main flow method of AutoConfiguration. Each line of this method can be regarded as a step, so the processing steps are as follows:

1. Load the @enableAutoConfiguration attribute value getAttribute method:

protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
    String name = getAnnotationClass().getName();
    AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
    Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
        + " annotated with " + ClassUtils.getShortName(name) + "?");
    return attributes;
}
Copy the code

2. Get the meta-inf/spring. Factories file to @ EnableAutoConfiguration fully qualified class name for the key value, getCandidateConfigurations method:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
        getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
        + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}
protectedClass<? > getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;
}
Copy the code

. Among them, the SpringFactoriesLoader loadFactoryNames () the role of this method is to use the given class loader from the meta-inf/spring. The load of a given type factories factory implementation of a fully qualified class name;

3. To weight;

4. Get the class names of the classes to be excluded. These classes can be configured in the @enableAutoConfiguration annotation.

5. Check the two collections;

6. Remove the classes to be excluded.

7. Filter according to OnBeanCondition, OnClassCondition and other conditions (interested in further understanding);

8. Broadcast events, AutoConfigurationImportListener, all implementation class, and then generate an event broadcast;

9. Encapsulate the fully qualified names of classes that need to be assembled and excluded as AutoConfigurationEntry.

Therefore, @enableAutoConfiguration can be summarized simply as: Search the meta-INF/Spring. factories Configuration files from the classpath and reflectively instantiate the EnableAutoConfiguration Configuration items into the corresponding IoC container Configuration classes tagged with @Configuration. And loaded into the IoC container.

Third, summary

Through the above analysis @ SpringBootApplication annotation of operation by @ SpringApplicationConfiguration statement was noted for the configuration, To be AnnotationConfigApplicationContext scans and initialize the Spring container.

Use @enableAutoConfiguration to scan, filter, and load the required components. Scan and register all classes that annotate @Component and its child annotations with @ComponentScan. Working together, these annotations enable the powerful automatic configuration capabilities of the SpringBoot project.

The above is all the content of this summary, I hope to help you. If there are omissions and mistakes, please do not hesitate to correct.

Author: Vivo Internet Development team -Peng Peng