SpringBoot solves the pain point of cumbersome configuration of spring and spring MVC, and realizes automatic assembly based on the principle of "Convention is greater than configuration". Let's explore the principle of SpringBoot automatic assembly.
1, What is assembly
Putting beans into the Ioc container of Spring is called assembly. When assembling beans, we first need to know which classes need to be assembled. Generally speaking, there are two ways to implement this method, one is the traditional xml method, and the other is the annotation method. The following describes how to realize assembly through annotation.
Starting principle
We found that any springboot project will have the following startup class:
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
In order to understand the principle of SpringBoot, let's start with Annotation and see what's done in @ SpringBoot application? Open the spring boot application Annotation, and you can see that it is actually a composite Annotation
@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 { ... }
Spring boot application essentially consists of three annotations, namely
- @Configuration (@ configuration also used in @ SpringBootConfiguration)
- @EnableAutoConfiguration
- @ComponentScan
We can directly use these three annotations or start the springboot application. However, it is cumbersome to configure three annotations each time, so it is more convenient to directly use a composite annotation. Then carefully observe the three annotations. Except that EnableAutoConfiguration may be a little strange, the other two annotations are used a lot.
@Configuration
You should have used the annotation Configuration. It is an annotation used by the Configuration class based on the Spring IOC container in the form of JavaConfig. Because SpringBoot is essentially a spring application, it is normal to load the Configuration of IoC container through this annotation. So @ Configuration is marked in the startup class, which means that it is also a Configuration class of IoC container.
Spring applications in the traditional sense are based on xml to configure bean dependencies. Then initialize the beans at startup through the spring container. If there are dependencies between beans, analyze the beans already in the IoC container and assemble them according to the dependencies. Until the introduction of Annotations in Java 5, the spring framework followed the big stream and introduced a dependency binding description method based on Java code and Annotation meta information, that is, JavaConfig.
Since spring 3, spring has supported two bean configuration methods, one is based on xml file, and the other is JavaConfig.
Any Java class definition marked with @ Configuration is a JavaConfig Configuration class. In this Configuration class, the return value of any method marked with @ bean will be registered in the IOC container of Spring as a bean definition, and the method name will become the id of the bean by default
@ComponentScan
@The ComponentScan annotation is the most frequently contacted annotation, which is equivalent to < context: component scan / > in the xml configuration file. Its main function is to scan the classes under the specified path, identify the classes to be assembled, and automatically assemble them into the Ioc container of spring.
The form of identifying the class to be assembled is mainly: @ Component, @ Repository, @ Service, @ Controller and other annotation identified classes;
ComponentScan will scan all classes marked with relevant annotations under the current package into the IoC container by default.
@EnableAutoConfiguration
Enable is not new
In spring version 3.1, a series of notes beginning with @ Enable are provided. The Enable host should be further improved on the JavaConfig framework. Yes, users can avoid configuring a large amount of code to reduce the difficulty of using the spring related framework.
For example, @ enableascheduling, @ EnableCaching, @ EnableWebMvc, etc., the concept and way of doing things of @ EnableAutoConfiguration come down in one continuous line. A brief summary is to collect and register bean definitions related to specific scenarios with the support of @ Import.
- @EnableWebMvc, this annotation introduces all the bean s needed by the MVC framework in Spring applications
- @Enablesscheduling loads the bean definitions related to the Spring scheduling framework into the IoC container through @ Import to enable the support of scheduling tasks.
With the help of @ Import, @ EnableAutoConfiguration loads all qualified @ Configuration configurations into the IoC container created and used by the current SpringBoot. That's it!
@EnableAutoConfiguration will automatically configure the project according to the jar dependency in the classpath. For example, if Spring Boot starter web dependency is added, the dependency of Tomcat and Spring MVC will be automatically added, and Spring Boot will automatically configure Tomcat and Spring MVC.
@As a composite Annotation, EnableAutoConfiguration defines the following key information:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { ... }
@Import(AutoConfigurationImportSelector.class) can be guessed from its name that it implements the loading function based on dynamic bean s based on ImportSelector. You should know how the Springboot @Enable * annotation works. The array (full class name of the class) returned by selectImports of the ImportSelector interface will be included in the spring container. You can guess that the implementation principle here must be the same. Locate the selectImports method in the AutoConfigurationImportSelector class.
In essence, EnableAutoConfiguration will help SpringBoot applications load all @ Configuration compliant configurations into the IoC container created by the current SpringBoot, which is supported by a tool class springfactoryesloader provided by the Spring framework. The @ Conditional annotation provided by Spring is used to selectively filter the bean s to be loaded
AutoConfigurationImportSelector
Let's look at the selectImports method under the source code of AutoConfigurationImportSelector
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } //Load the data in the spring-autoconfigure-metadata.properties configuration file AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); //Get the class that needs to be loaded dynamically through getAutoConfigurationEntry AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); //Return the classes that need to be injected into spring return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
Source code of AutoConfigurationMetadataLoader:
final class AutoConfigurationMetadataLoader { 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 { 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); } /** * {@link AutoConfigurationMetadata} implementation backed by a properties file. */ private static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata { private final Properties properties; PropertiesAutoConfigurationMetadata(Properties properties) { this.properties = properties; } @Override public boolean wasProcessed(String className) { return this.properties.containsKey(className); } @Override public Integer getInteger(String className, String key) { return getInteger(className, key, null); } @Override public Integer getInteger(String className, String key, Integer defaultValue) { String value = get(className, key); return (value != null) ? Integer.valueOf(value) : defaultValue; } @Override public Set<String> getSet(String className, String key) { return getSet(className, key, null); } @Override public Set<String> getSet(String className, String key, Set<String> defaultValue) { String value = get(className, key); return (value != null) ? StringUtils.commaDelimitedListToSet(value) : defaultValue; } @Override public String get(String className, String key) { return get(className, key, null); } @Override public String get(String className, String key, String defaultValue) { String value = this.properties.getProperty(className + "." + key); return (value != null) ? value : defaultValue; } } }
The above selectImports method returns some beans that need to be automatically assembled by springboot, and returns the name of the bean to be assembled in the form of String []. However, this method still does a lot of operations before actually returning the name of the bean to be assembled. Did some dynamic filtering operations.
The first step is to load the spring-autoconfigure-metadata.properties file under the current classpath through loadMetadata, which configures all dynamic loading conditions. The second step is to obtain the class to be loaded dynamically through getAutoConfigurationEntry. The specific source code of this step is as follows:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); //The next step is to load the instances in the spring.factories file under the classpath 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); }
The detailed source code of getCandidateConfigurations is as follows:
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; }
Spring factory loading mechanism
Spring factory loading mechanism, namely Spring Factories Loader, the core logic is to use Spring Factories Loader to load classes implemented by users and configure them under the agreed META-INF/spring.factories path. This mechanism can dynamically add extensions to the framework context.
Similar to Java SPI, this mechanism provides users with extensible hooks to achieve the custom extension function of the framework.
Here, the function of SpringFactoriesLoader is to load the corresponding classes into the spring IoC container according to the key from the classpath/META-INF/spring.factories file.
You can see that this is the content of all spring. Factories files under the current classpath. The following is the configuration of EnableAutoConfiguration in spring.factories. If there is no filtering operation of AutoConfigurationImportSelector, all configured values here will be returned to the IOC container in the getCandidateConfigurations method, and springboot will automatically load these classes.
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
When the SpringBoot project is started, the above XXAutoConfiguration is loaded to realize automatic assembly.
Summary:
@The function of EnableAutoConfiguration is to search all META-INF/spring.factories Configuration files from the classpath, instantiate the Configuration item corresponding to org.springframework.boot.autoconfigure.EnableutoConfiguration into the corresponding IoC container Configuration class in the form of JavaConfig marked with @ Configuration through reflection, and then summarize it into one and load it into the IoC container. If these function Configuration classes want to take effect, they will go to the classpath to find out whether there is a dependent class of this class (that is, pom.xml must have a jar package for the corresponding function), and the default attribute value class is injected into the Configuration class, which can be referenced and assigned with default values. The principle of generating function classes is that customization takes precedence, and automatic assembly classes will be used only when there is no customization.