One, foreword

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

How does this annotation affect the startup and automatic configuration of SpringBoot? In this article, we will break down the source code for you and uncover the mystery behind the @SpringBootApplication annotation.

Second, the body

I am very interested in the automatic configuration of SpringBoot project, so I learned its source code and organized some of it. If there is any error, please correct it.

The @SpringBootApplication annotation has the following source code:

@ComponentScan(excludeFilters = {
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

As you can see, this is a composite annotation with seven different annotations, which are analyzed below.

2.1 annotations

2.1.1 Note 1: @target ({elementType.type})

Type is used to indicate the scope of an annotation. Type indicates that the scope is a class or interface.

2.1.2 Note 2: @Retention(retentionpolicy.runtime)

2.1.3 Note 3: @Documented

Indicates that the comment was recorded by Javadoc.

2.1.4 Note 4: @inherited

In the annotation, when the parent class adds the @SpringBootApplication annotation, the child class inherits the annotation (not for the implementation class of the interface).

2.1.5 Note 5: @SpringBootConfiguration

The bottom layer is still @Configuration annotation, the source code is as follows:

public @interface SpringBootConfiguration {

2.1.6 Note 6: @ComponetScan

@ComponentScan is a very important annotation in Spring. It is a Component of the XML configuration that @ComponentScan can automatically scan and load components (e.g., @Component, @Repository, etc.) or bean definitions that meet the requirements. Finally, these bean definitions are loaded into the IOC container.

You can fine-grained customize the scope of @ComponentScan automatic scanning 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. Therefore, it is best to place the SpringBoot boot class under the root package, because basePackages are not specified by default.

2.2 annotation: @EnableAutoConfiguration

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

Its 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 {}; }

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

2.2.1 Comment: @AutoConfigurationPackage

The source is as follows:

public @interface AutoConfigurationPackage {

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. Moving on to the Registrar class:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {@Override // Metadata public void registerBeanDefinitions(annotationMetadata); BeanDefinitionRegistry) {// Register all the components in the package that we annotate (registry, registry). new PackageImport(metadata).getPackageName()); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); }}

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); }}

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

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 JARs and components that we configured in the pom.xml file. So the method returns an array of Strings in the full path of the Class, and the returned classes are managed by the Spring container. The method source 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()); }

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 has the following source code:

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

This method by EnableAutoConfiguration. ENABLED \ _OVERRIDE \ _PROPERTY determine whether need to automatically configure the configuration items, the default is true. The loadMetadata method source code is as follows:

protected static final String PATH = "META-INF/" + ""; 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); }

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.

The file is located in 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); }

This method is the main flow method of AutoConfiguration. You can think of each line of this method as a step. The processing steps are as follows:

1. Load attribute value with @EnableAutoConfiguration annotated 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; }

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; } protected Class<? > getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }

. 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 name of the classes to be excluded, which can be configured in the @EnableAutoConfiguration annotation;

5. Check the two collections;

6. Remove classes that need to be excluded;

7. Filtering according to OnBeanCondition, OnClassCondition and other conditions (you can know more about it if you are interested);

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

9. Encapsulate the fully qualified name of the class to be assembled and excluded into an AutoConfigurationEntry object.

Therefore, @EnableAutoConfiguration can be simply summarized as: Search all the META-INF/spring.factories Configuration files from the classpath and instantiate the Configuration items corresponding to enableAutoConfiguration into the corresponding IoC container Configuration class marked @Configuration by reflection. 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.

Scan, filter, and load required components with @EnableAutoConfiguration; Scan and register all classes labeled with @Component and its subannotations with @ComponentScan; Working together, these annotations enable the powerful automatic configuration capabilities of the SpringBoot project.

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

Author: Vivo Internet development team -Peng Peng