2.2.2 Automatic Configuration
The ability to automatically configure for us when we add jar package dependencies allows us to run written projects without configuring or with a small number of configurations.
Question: How on earth does Spring Boot automatically configure its components?
2.2.2.1 @SpringBootApplication
The entry where the Spring Boot application starts is the main method of the @SpringBootApplication annotation class.
@SpringBootApplication scans Spring components and automatically configures Spring Boot
@SpringBootApplication public class LearnApplication { public static void main(String[] args) { SpringApplication.run(LearnApplication.class, args); } }
@SpringBootApplication annotation class
// Scope of application of annotations: classes, interfaces, enumerations @Target({ElementType.TYPE}) // Life cycle of annotations: Runtime @Retention(RetentionPolicy.RUNTIME) // Label annotations can be labeled in javadoc @Documented // Indicates that annotations can be inherited by subclasses @Inherited // Mark this class as a configuration class @SpringBootConfiguration // Start auto-configuration @EnableAutoConfiguration // Packet Scanner @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication { @AliasFor( annotation = EnableAutoConfiguration.class ) Class<?>[] exclude() default {}; @AliasFor( annotation = EnableAutoConfiguration.class ) String[] excludeName() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackages" ) String[] scanBasePackages() default {}; @AliasFor( annotation = ComponentScan.class, attribute = "basePackageClasses" ) Class<?>[] scanBasePackageClasses() default {}; }
As you can see above, the @SpringBootApplication annotation consists mainly of three core annotations @SpringBootConfiguration, @EnableAutoConfiguration, @ComponentScan.
2.2.2.1.1 @SpringBootConfiguration
The @SpringBootConfiguration annotation states that its class is the Spring Boot configuration class
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented // Configure to IOC Container @Configuration public @interface SpringBootConfiguration { }
As you can see from the above, the @SpringBootConfiguration annotation class is mainly annotated with the @Configuration annotation, which is provided by the Spring framework to indicate that the current class is a configuration class and can be scanned by the component scanner.
2.2.2.1.2 @EnableAutoConfiguration
The @EnableAutoConfiguration annotation is represented as an auto-configuration class, which is the most important annotation for Spring Boot and is also the annotation that implements auto-configuration.
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited // Automatic Configuration Package @AutoConfigurationPackage // Automatically configure scan import @Import({AutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
From the source code, you can see that the @EnableAutoConfiguration annotation is a combined annotation that imports beans that need to be registered with IOC for a particular scene with the @Import annotation and loads them into the IOC container.The @AutoConfiguration Package uses @Import to collect all Bean definitions that meet the automatic configuration criteria and load them into the IOC container.
-
@AutoConfigurationPackage
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited // Import components registered in Registrar @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
From the above source code, you can see that the @AutoConfiguration Package annotation functions with the @Import annotation.@Import is the underlying Spring Framework comment that imports a component class to a container
Registrar Class Source
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // Scan the package in which the main program class resides and the components under all subpackages into the Spring container register(registry, new PackageImport(metadata).getPackageName()); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); } }
As you can see from the above, the @AutoConfiguration Package annotation is designed to load the package in which the main program class resides and the components under all subpackages into the IOC container.
Therefore, when defining a project package directory, it is required that the package structure defined be canonical, that the project main program startup class be placed in the outermost root directory location, and then subpackages and classes are built inside the root directory location for business development, so that the defined classes can be scanned by the component scanner.
-
@Import({AutoConfigurationImportSelector.class})
Import the AutoConfigurationImportSelector class into the Spring container.
AutoConfiguration ImportSelector can help Spring Boot applications import all eligible @Configuration configurations into the IOC container (ApplicationContext) that the current Spring Boot creates and uses.
// The process of automatic configuration 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())); // Get the auto-configured configuration class AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector) .getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } } // Get Auto Configuration Meta Information private AutoConfigurationMetadata getAutoConfigurationMetadata() { if (this.autoConfigurationMetadata == null) { // Loading auto configuration meta information requires passing in the beanClassLoader class loader this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); } return this.autoConfigurationMetadata; } // Get the auto-configured configuration class protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); // From META-INF/Spring.factoriesGet the auto-configuration class for in the configuration file to 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); }
Deep into AutoConfigurationMetadataLoader.loadMetadata() Method
// Class path of configuration class to be loaded in file protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties"; private AutoConfigurationMetadataLoader() { } public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) { return loadMetadata(classLoader, PATH); } static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { try { // Read spring-boot-autoconfigure-2.1.14.RELEASE.jar Middle spring-autoconfigure-Metadata.propertiesInformation Generation URL 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); } }
Deep into AutoConfigurationImportSelector.getCandidateConfigurations() Method
This method has an important loadFactoryNames method that lets SpringFactoriesLoader load the names of some components.
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // This method requires two parameters, getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader() // getSpringFactoriesLoaderFactoryClass() returns: EnableAutoConfiguration.class // Return from getBeanClassLoader(): beanClassLoader class loader 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; } protected ClassLoader getBeanClassLoader() { return this.beanClassLoader; }
Continue to dig deeper into the loadFactoryNames() method
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { // Acquired health String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { // If the class loader is not empty, load META-INF/Spring.factoriesEncapsulates the class path information of the configuration class set in it as an Enumeration object Enumeration<URL> urls = (classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories")); 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 factoryClassName = ((String) entry.getKey()).trim(); for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
Will read oneSpring.factoriesFile, read without error
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
It actually loads an external file that is
The @EnableAutoConfiguration annotation is to search for META-INF/Spring.factoriesConfiguration file, andOrg.springframework.boot.Autoconfigure.EnableAutoConfigurationThe corresponding JavaConfig configuration class labeled @Configuration is instantiated by reflection for the configuration and loaded into the IOC container.
In the case of a web project, a web environment dependency launcher is added to the project, corresponding to theOrg.springframework.boot.Autoconfigure.web.servletThe.WebMvcAutoConfiguration auto-configuration will take effect, and when you turn on the auto-configuration, you will find that the Spring MVC is fully annotated in the configuration classThe default configuration of the environment required for the running environment includes prefix, suffix, attempt parser, MVC validator, and so on.
summary
Steps for underlying Spring Boot auto-configuration:
1) Spring Boot application startup
2)@SpringBootApplication comment works
3)@EnableAutoConfiguration comment works
4)@AutoConfiguration Package comment works
The main purpose of the @AutoConfiguration Package comment is @Import({AutoC)OnfigurationPackages.Registrar.class}), which is imported into the container through the Registrar class, which scans the packages and subpackages of the main configuration class and imports the corresponding components into the IOC container.
5)@Import(AutoConfigurationImportSelector.class)
@Import (AutoConfigu)RationImportSelector.class) It imports the AutoConfigurationImportSelector class into the container, AutoConfigurationImportSelectorThe purpose of a class is to use the internal tool class SpringFactoriesLoader to find META-INF/s in all jar packages on the classpath during execution through the getAutoConfiguration Entry() methodSpring.factoriesLoading implements a series of container creation processes that communicate configuration information to the Spring Factory loader.
2.2.2.1.3 @ComponentScan
@ComponentScan annotates the path of the specific scan package, determined by the location of the package where the Spring Boot main program is located.During the scan, the @AutoConfiguration Package annotation parses the package to get the location of the Spring Boot main program class