SpringBoot自动装配原理详细讲解(清楚 明白)
Posted 技术无产者
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot自动装配原理详细讲解(清楚 明白)相关的知识,希望对你有一定的参考价值。
注意看代码加的中的注解
1.启动类上因为加上了 @EnableEurekaServer这个注解 才可以实现自动装配
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication
public static void main(String[] args)
SpringApplication.run(EurekaApplication.class);
2.自动装配的核心方法 loadSpringFactories()
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
// 注意这点 loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) 方法
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader)
String factoryClassName = factoryClass.getName();
// 注意这点 loadSpringFactories方法
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
// 这点才是真正从文件中加载需要自动装配类的业务逻辑
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
// 省略
。。。。。。。。。。。。。。。
cache.put(classLoader, result);
return result;
catch (IOException var13)
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
3.loadFactoryNames()方法
注意这个方法是进入从META-INF/spring.factories文件下加载需要自动装配类的核心方法,但是这个方法会被调用很多次,比如:
1> SpringApplication.run(EurekaApplication.class) 中进入loadFactoryNames
//1. 进入 SpringApplication的构造方法中
SpringApplication.run(EurekaApplication.class)
// 2. 注意getSpringFactoriesInstances()方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources)
// 。。。 省略
// 光这里就调用了 getSpringFactoriesInstances()方法两次 但注意这里传入的参数类型不同
// 下面会用到这点差异
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
//3.再次进入到 pringFactoriesLoader.loadFactoryNames(type, classLoader)方法
// 注意看loadFactoryNames方法每次传入一个type
//光上次在构造函数中就传过来两个不同的类型 ApplicationContextInitializer.class
// 分别是 1> ApplicationListener.class 2>
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type)
return this.getSpringFactoriesInstances(type, new Class[0]);
// 再次进入到 pringFactoriesLoader.loadFactoryNames(type, classLoader)方法
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args)
ClassLoader classLoader = this.getClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// ....
return instances;
2>通过@SpringBootApplication注解进入loadFactoryNames方法
// 1.进入@SpringBootApplication 注解
@Target(ElementType.TYPE)
//..
@SpringBootConfiguration
@EnableAutoConfiguration
// ....
)
public @interface SpringBootApplication
// ....
// 2. 进入到@EnableAutoConfiguration注解
// 在这个注解里看到了 熟悉的@Import注解
// 自动注入就是通过这个注解对配置了@Configure的自动装配类进行自动注入的
//...
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
//...
// 3. 进入 AutoConfigurationImportSelector类
// 直接看 核心方法 selectImports
public String[] selectImports(AnnotationMetadata annotationMetadata)
if (!this.isEnabled(annotationMetadata))
return NO_IMPORTS;
else
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata)
if (!this.isEnabled(annotationMetadata))
return EMPTY_ENTRY;
else
//...
// 注意这个方法 getCandidateConfigurations(annotationMetadata, attributes);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
// 4.进入 getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes)
// 在这里看以看到 这里通过 this.getSpringFactoriesLoaderFactoryClass()获得类的类型 传入
// loadFactoryNames方法中
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
到这里就展示了两种进入loadFactoryNames方法的途径,接下来解析这个方法:
a.可以看到这行代码,每次根据传入到loadFactoryNames方法里的类的类型获得类的全限定类名
String factoryClassName = factoryClass.getName();
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader)
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
b.loadSpringFactories方法从META-INF/spring.factories加载需要装配的类
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
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements())
cache.put(classLoader, result);
return result;
// 看下面debug 结果 result得到39种类型
catch (IOException var13)
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
spring-boot-autoconfigure-2.1.4.RELEASE.jar\\META-INF\\spring.factories 下面只有6种类型,在其它包下的META-INF\\spring.factories一定还有对应的33种
点开 EnableAutoConfiguration可以发现, 这242个才是SpringBoot提供的所有自动装配的类文件
再根据这个方法,我们可以知道 先获得factoryClass的名字,也就是那39个类的名字,然后根据这个名字去获得下面对应的类文件,所以可知当我们需要EnableAutoConfiguration的类时,根据它的名字去result中直接加载,SpringBoot只在第一次调用LoadFactoryNames方法时去spring.factories文件下扫描到result中存储,下次再访问时就不会再扫描磁盘,而是根据类的名字获取下面所有的类了,看下面这个判断:
if (result != null) return result; else try
4.上面从META-INF\\spring.factories文件加载类的逻辑说清楚后,再看@import获得自动装配类的逻辑
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata)
if (!this.isEnabled(annotationMetadata))
return EMPTY_ENTRY;
else
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 根据 org.springframework.boot.autoconfigure.EnableAutoConfiguration 这个类的名字
// 获得下面SpringBoot提供的242个可以自动装配的类
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 这里通过
// org.springframework.boot.autoconfigure.condition.OnBeanCondition,\\
// org.springframework.boot.autoconfigure.condition.OnClassCondition,\\
// org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
// 这三个类对这242个类进行过滤 也就是@Condition注解 filiter 里面是具体的逻辑
// 过滤后返回 通过@ImportSelector即可自动创建对象到IOC
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
以上是关于SpringBoot自动装配原理详细讲解(清楚 明白)的主要内容,如果未能解决你的问题,请参考以下文章
程序员必备技能之SpringBoot的自动装配原理,很详细,建议收藏!!!
程序员必备技能之SpringBoot的自动装配原理,很详细,建议收藏!!!
掌握了SpringBoot的自动装配原理后你会发现自定义Starter也是非常容易的哦!