Using Spring Boot
First open IDEA to create a Spring Boot project
Select SpringInitializer and then the next process is not cumbersome.
Then open the pom file and we found that Srping Boot has a configuration dependency
xml复制代码启动依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
测试依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
After clicking the startup dependency, we found that there are various dependencies. The
original startup dependency includes spring-boot-starter startup dependency, mvc dependency, tomcat and json conversion package, and validator verification package, etc.
xml复制代码<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
Then there is a main program in the root directory, the startup class
java复制代码@SpringBootApplication
public class SpringDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDemoApplication.class, args);
}
}
If the main startup class is not marked by @SpringBootApplication, an error will be reported at startup: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
@SpringBootApplication startup annotation
After clicking in, I found that this is a combined annotation
java复制代码
@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 {
....
}
Found that there are three annotations @SpringBootConfiguration
@EnableAutoConfiguration @ComponentScan
It can be explained that the @SpringBootApplication annotation is a combined annotation.
We can also use @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan on the startup class to start Spring Boot
Let's first look at the @ComponentScan annotation
java复制代码@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
The function of this annotation is to scan the path under the specified package, specifying two filter conditions TypeExcludeFilter and AutoConfigurationExcludeFilter
These two should filter some packages at startup or add some components to the container at startup
There is a match method in the source code of the TypeExcludeFilter class to judge the logic of filtering
kotlin复制代码 public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
if (this.beanFactory instanceof ListableBeanFactory && this.getClass().equals(TypeExcludeFilter.class)) {
Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory)this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
Iterator var4 = delegates.iterator();
while(var4.hasNext()) {
TypeExcludeFilter delegate = (TypeExcludeFilter)var4.next();
if (delegate.match(metadataReader, metadataReaderFactory)) {
return true;
}
}
}
return false;
}
if (this.beanFactory instanceof ListableBeanFactory This sentence can see the underlying Ioc container of BeanFactory to execute the custom filtering method TypeExcludeFilter is used for extended component filtering
Similarly, there is also a match method in AutoConfigurationExcludeFilter. You can see this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader) in the source code to judge whether it is configuration and automatic configuration, so don't pay attention for now.
java复制代码
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
private ClassLoader beanClassLoader;
private volatile List<String> autoConfigurations;
public AutoConfigurationExcludeFilter() {
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader);
}
private boolean isConfiguration(MetadataReader metadataReader) {
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
}
private boolean isAutoConfiguration(MetadataReader metadataReader) {
return this.getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
}
protected List<String> getAutoConfigurations() {
if (this.autoConfigurations == null) {
this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader);
}
return this.autoConfigurations;
}
}
Then look at the @EnableAutoConfiguration annotation
java复制代码@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
You can see that there are @AutoConfigurationPackage and
@Import({EnableAutoConfigurationImportSelector.class})
These two annotations
First look at the @AutoConfigurationPackage annotation. From the name, it is an automatic configuration package. After clicking in,
@Import({Registrar.class}) public @interface AutoConfigurationPackage { } finds that @Import({Registrar.class}) imports a component for the @ annotation.
Registrar component
java复制代码 @Order(-2147483648)
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
After debugging, you can see that the function of this annotation is to import the package name under our root directory, and import components through the package name, that is, scan various beans and register them in the Ioc container.
Then let's study @Import({EnableAutoConfigurationImportSelector.class})
It seems that this is also an import component
Open the EnableAutoConfigurationImportSelector class
java复制代码public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector
Then open AutoConfigurationImportSelector and you will see the following code
java复制代码public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
This class implements the DeferredImportSelector interface and you will find that this class inherits the ImportSelector class
java复制代码public interface DeferredImportSelector extends ImportSelector
The function is that the execution time of DeferredImportSelector is later than that of ImportSelector, and after importing the component, it will search for the result of the imported component.
Then we also found that the following code selectImports method looks for imported components by name, which also proves the significance of AutoConfigurationImportSelector inheriting the DeferredImportSelector interface.
java复制代码 public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
configurations = this.sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return (String[])configurations.toArray(new String[configurations.size()]);
} catch (IOException var6) {
throw new IllegalStateException(var6);
}
}
}
You can see the following code
java复制代码List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
It seems that a configuration collection is configured
After clicking in, I found the following code
java复制代码SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
this.getSpringFactoriesLoaderFactoryClass()
java复制代码 protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
The incoming Class is @EnableAutoConfiguration, and in this class
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; enable automatic configuration
SpringFactoriesLoader.loadFactoryNames scans the classpath
Use classLoader to load resources under the specified constant path META-INF/spring.factories
We will find that spring.factories files are loaded under multiple packages
Then debug continues to go down and finds
In the selectImports method of the AutoConfigurationImportSelector class, configurations = this.filter(configurations, autoConfigurationMetadata); this code performs filter conditions
The incoming parameter autoConfigurationMetadata is the above code AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); The automatic configuration condition query results of all automatic configuration classes
Click in and find that all the spring-autoconfigure-metadata.properties files are loaded,
and then find that this file basically has a full path package name starting with org.springframework.boot.autoconfigure, and I understand it instantly
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
This class is to find all the automatic configuration classes supported by Spring Boot
Then filter whether the loaded automatic configuration package contains imports under the class path
AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next(); Automatic configuration import filter
Exclude the automatic configuration conditions that do not exist in the class path, and finally add 20 default automatic configurations to the Ioc container
Summarize
Basically, the automatic configuration principle of Spring Boot is roughly divided into three steps:
- 1 The filter first filters on the startup class or adds components to the container
- 2 Scan all beans under the root path
- 3 Add the class path META-INF/spring.factories and META-INF/spring-autoconfigure-metadata.properties automatically configured packages and filters whether the automatic configuration has packages under the class path and finally registered in Ioc