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