This article begins by showing you how to customize a SpringBoot starter and use it in your project. Then the principle of springboot automatic configuration is briefly explained.

This article is based on SpringBoot 2.2.4.release

Customize a starter step

May refer to: www.pdai.tech/md/spring/s…

Do it when you have time

The principle of automatic configuration of the starter

Start with the conclusion:

Through the AutoConfigurationImportSelector @ EnableAutoConfiguration on import, The selectImports() method scans all jars with Meta-INF/Spring.Factories using the SpringFactoriesLoader. The spring.factories file is a set of key= values, where one key is the full class name of the EnableAutoConfiguration class and its value is a list of xxxxAutoConfiguration class names, These class names are separated by commas.

In SpringApplication. Run (…). Internally, the selectImports() method is executed to find the classes corresponding to the fully qualified names of all JavaConfig auto-configuration classes, and then all auto-configuration classes are loaded into the Spring container.

In each of the starter has a xxxxAutoConfiguration class, the class will be loaded into the container, the class has an annotation on @ EnableConfigurationProperties configuration class will be the starter is just as loaded into the container, This will read the configuration items in Application.yml and assign them to the Properties class.

Spring Boot uses the @enableAutoConfiguration annotation to find and load all the auto-configuration classes in the meta-INF/Spring. factories configuration file. These AutoConfiguration classes are named after the AutoConfiguration end, which is actually a JavaConfig Spring container configuration class. It can use the class named after the Properties end to obtain the Properties configured in the global configuration file such as: Server.port, and the XxxxProperties class is bound to the corresponding properties in the global configuration file via the @ConfigurationProperties annotation.

annotations@SpringBootApplication

@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

This annotation is placed on the startup class and contains multiple annotations, which we will look at one by one.

annotations@SpringBootConfiguration

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

In fact, it’s just another declaration of @Configuration that identifies the SpringBoot Configuration class. This class is annotated with @Configuration.

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

annotations@EnableAutoConfiguration

According to the literal meaning, start automatic configuration, this look at the name should be the focus, first look at the source code.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/** * Exclude specific auto-configuration classes such that they will never be applied. */Class<? >[] exclude()default {};

	/** * Exclude specific auto-configuration class names such that they will never be * applied. */
	String[] excludeName() default {};

}
Copy the code

As you can see from the properties, if some auto-configuration classes are not needed, you can manually exclude them here. So let’s look at the annotation @autoConfigurationPackage.

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

}
Copy the code

This class is described as follows and represents the package path of the annotation class to be registered. Another annotation, @import, is used here.

Indicates that the package containing the annotated class should be registered with















The above means need to be loaded AutoConfigurationPackages. The Registrar. The class to the container, the Registrar translation as the Registrar, for translation for “automatic configuration package name registration member”.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interfaceImport { Class<? >[] value(); }Copy the code

The bootstrap breakpoint indicates that the bootstrap package is registered as the root package name of the component. In other words, only components under the package of the main spring-boot class (class annotated by @SpringBootApplication) can be loaded into the container.

annotationsAutoConfigurationImportSelector

There’s a process method in there

@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
            () -> String.format("Only %s implementations are supported, got %s",
                    AutoConfigurationImportSelector.class.getSimpleName(),
                    deferredImportSelector.getClass().getName()));
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
            .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata); }}Copy the code

Here focus on getAutoConfigurationEntry method, further check, will call the following the loadSpringFactories method.

Public static final String FACTORIES_RESOURCE_LOCATION = “meta-INF /spring.factories”;

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if(result ! =null) {
			return result;
		}

		try{ Enumeration<URL> urls = (classLoader ! =null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for(Map.Entry<? ,? > entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex); }}Copy the code

Each starter defines a class in meta-INF/Spring. factories that needs to be loaded. Then SpringBoot automatically reads the AutoConfiguration classes and loads them into the container. This enables automatic configuration.

reference

  • Principle of SpringBoot automatic configuration
  • SpringBoot Custom starter