SpringBoot自动装配原理(还在完善)

SpringBoot自动装配原理(还在完善),第1张

首先从springBoot启动类入口@SpringBootApplication进来

可以看到有七个组合注解,上面四个元数据注解就不多说了,接着往@SpringBootConfiguration这个注解进去可以看有一个@Configuration注解,这个注解的意思也就是表明了是一个配置类,那么也就意味着我们的@SpringBootApplication注解也是一个配置类

@ComponentScan注解用来扫描该类父包下面的所有子包,

那么以上注解讲完,剩下一个@EnableAutoConfiguration(开启自动配置)注解也就是来做我们SpringBoot自动装配干活的地方了,我们接着进去往下看

进来后发现也有四个元注解,以及@AutoConfigurationPackage这个注解的作用是将 添加该注解的类所在的package 作为 自动配置package 进行管理。

重点来了:注意看@Import注解引入了一个AutoConfigurationImportSelector类,@Import的作用就是替代了Spring老版本的Import标签,来给我们引入其他的配置类,@Enable..注解的核心就是用@Import注解向Spring注入其他bean以及配置类。继续进去AutoConfigurationImportSelector类可以看到实现了ImportSelector接口

实现了selectImports方法,这个方法返回的是一个String[],返回的数组Spring会帮我们注入到IOC容器里面,

那么我们接着往这个方法去看,getAutoConfigurationEntry(annotationMetadata); 那么在这里的话 我们从代码往上从下说一下他都干了什么。

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //获取@EnableAutoConfiguration注解扫描到的要去装配的类
   List configurations = getCandidateConfigurations(annotationMetadata, attributes); 
    //这里首先是第一步过滤要自动装配的Bean,移除重复的
   configurations = removeDuplicates(configurations); 
    // 排除需要排除的类,具体 *** 作是通过@SpringBootApplication 注解中的 exclude、excludeName、环境属性中的spring.autoconfigure.exclude配置
   Set exclusions = getExclusions(annotationMetadata, attributes); 
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
    //这里是根据spring-autoconfigure-metadata.properties 中配置的规则来过滤一些不用装配进去的类
   configurations = getConfigurationClassFilter().filter(configurations);
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return new AutoConfigurationEntry(configurations, exclusions);
}

那么再接着讲spring-autoconfigure-metadata.properties这个配置的规则,其实都是去基于@conditional这个注解去判断是否要装配进容器。这个注解里面必须放入一个实现了Condition 这个接口的类然后实现他的matches方法,也就是matches方法返回ture则装配到容器。

接着进入getCandidateConfigurations()方法。大家可以翻译一下下面的一句信息,也就是(在 META-INFspring.factories 中找不到自动配置类。如果您“+”正在使用自定义包装,请确保该文件是正确的。)

protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List 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;
}

在这里就可以发现其实就是SpringBoot基于Spi定义的一个规范,让想要通过Spring容器来管理对象的类在META-INF/spring.factories 写入要装载进来的类。接着打开SpringBoot的META-INF目录可以发现要装载的类文件和我们上面讲到的过滤文件在一个目录

接着打开Spring.factories 这个文件这些以AutoConfiguration结尾的类就是要去扫描装配的类

接着继续往下走具体是在哪个方法获取到这些文件的,接着进入getCandidateConfigurations()方法里面的SpringFactoriesLoader.loadFactoryNames()方法。

public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
   String factoryTypeName = factoryType.getName();
   return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

接着往下走进入这个loadSpringFactories(classLoader)

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
   MultiValueMap result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   try {
       //通过类加载器去扫描资源,FACTORIES_RESOURCE_LOCATION 这个常量就是META-INF/spring.factories
       //也就是意味着是在这里扫描META-INF/spring.factories这个文件
      Enumeration urls = (classLoader != null ?
            classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
            ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
      result = new LinkedMultiValueMap<>();
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         for (Map.Entry entry : properties.entrySet()) {
            String factoryTypeName = ((String) entry.getKey()).trim();
            for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
               result.add(factoryTypeName, factoryImplementationName.trim());
            }
         }
      }
      cache.put(classLoader, result);
      return result;
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
}

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/904958.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-15
下一篇2022-05-15

发表评论

登录后才能评论

评论列表(0条)

    保存