Briefly describe Spring Boot's automatic assembly

Speaking from the configuration file

When using Spring Boot, we know that Spring Boot has a global configuration file: application.properties or application.yml.

Our various attributes can be configured in this file, the most commonly configured such as: server.port, logging.level.*, etc. However, what we actually use is often only a small part, so are these attributes available? According to what? The answer is of course yes, these attributes can be found in the official documentation:

https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#common-application-properties

Then the question also followed, how does Spring Boot make these configurations effective in the project?

Start with the annotation of the startup class

First, let us focus our attention on the startup class. Does every startup class have this annotation-@SpringBootApplication This annotation is an indispensable annotation for Spring Boot projects. Then the principle of automatic configuration must be inextricably linked with this annotation!
@SpringBootApplication is a composite annotation or derived annotation. There is an annotation @EnableAutoConfiguration in @SpringBootApplication, which means to turn on automatic configuration when translating adult words. The definition is as follows:
Insert picture description here
In EnableAutoConfiguration, the AutoConfigurationImportSelector defined by SpringBoot is introduced through Import
. Just look at the main logic code


```java
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
    
    
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    
    
if (!isEnabled(annotationMetadata)) {
    
    
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata =
AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//主要逻辑在getAutoConfigurationEntry这个方法
AutoConfigurationEntry autoConfigurationEntry =
getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return
StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
    
    
if (!isEnabled(annotationMetadata)) {
    
    
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//通过getCandidateConfigurations方法获取所有需要加载的bean
List<String> configurations =
getCandidateConfigurations(annotationMetadata,
attributes);
//去重处理
configurations = removeDuplicates(configurations);
//获取不需要加载的bean,这里我们可以通过spring.autoconfigure.exclude人为配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
//发送事件,通知所有的AutoConfigurationImportListener进行监听
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
//这里是获取bean渠道的地方,重点看SpringFactoriesLoader#loadFactoryNames
protected List<String> getCandidateConfigurations(AnnotationMetadata
metadata,
AnnotationAttributes attributes) {
    
    
//这里的getSpringFactoriesLoaderFactoryClass()最终返回
EnableAutoConfiguration.class
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in METAINF/
spring.factories. If you "
+ "are using a custom packaging, make sure that file is
correct.");
return configurations;
}
}

As can be seen from the above logic, the final channel for obtaining beans is SpringFactoriesLoader.loadFactoryNames

public final class SpringFactoriesLoader {
    
    
public static final String FACTORIES_RESOURCE_LOCATION = "METAINF/
spring.factories";
private static final Log logger =
LogFactory.getLog(SpringFactoriesLoader.class);
private static final Map<ClassLoader, MultiValueMap<String, String>> cache =
new ConcurrentReferenceHashMap();
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable
ClassLoader classLoader) {
    
    
String factoryClassName = factoryClass.getName();
//通过factoryClassName获取相应的bean全称
//上面传入的factoryClass是EnableAutoConfiguration.class
return
(List)loadSpringFactories(classLoader).getOrDefault(factoryClassName,
Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable
ClassLoader classLoader) {
    
    
MultiValueMap<String, String> result =
(MultiValueMap)cache.get(classLoader);
if (result != null) {
    
    
return result;
} else {
    
    
try {
    
    
//获取工程中所有META-INF/spring.factories文件,将其中的键值组合成Map
Enumeration<URL> urls = classLoader != null ?
classLoader.getResources("META-INF/spring.factories") :
ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
    
    
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties =
PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
    
    
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName =
((String)entry.getKey()).trim();
String[] var9 =
StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
    
    
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
    
    
throw new IllegalArgumentException("Unable to load factories
from location [META-INF/spring.factories]", var13);
}
}
}
private static <T> T instantiateFactory(String instanceClassName, Class<T>
factoryClass, ClassLoader classLoader) {
    
    
try {
    
    
Class<?> instanceClass = ClassUtils.forName(instanceClassName,
classLoader);
if (!factoryClass.isAssignableFrom(instanceClass)) {
    
    
throw new IllegalArgumentException("Class [" + instanceClassName
+ "] is not assignable to [" + factoryClass.getName() + "]");
} else {
    
    
return ReflectionUtils.accessibleConstructor(instanceClass, new
Class[0]).newInstance();
}
} catch (Throwable var4) {
    
    
throw new IllegalArgumentException("Unable to instantiate factory
class: " + factoryClass.getName(), var4);
}
}
}

Each jar can define its own META-INF/spring.factories. When the jar is loaded, the
beans defined in spring.factories can be automatically loaded.
Insert picture description hereThis spring.factories file is also a group of key=value forms, One of the key is the full class name of the EnableAutoConfiguration class, and its value is a list of xxxxAutoConfiguration class names, which are separated by commas, as shown in the following figure:
Insert picture description here

Guess you like

Origin blog.csdn.net/qq_37669050/article/details/103404206