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也是非常容易的哦!

掌握了SpringBoot的自动装配原理后你会发现自定义Starter也是非常容易的哦!

SpringBoot项目开发技巧一:自动装配

spring boot自动装配原理