springboot启动流程 (3) 自动装配

打印 上一主题 下一主题

主题 937|帖子 937|积分 2811

在SpringBoot中,EnableAutoConfiguration注解用于开启自动装配功能。
本文将详细分析该注解的工作流程。
EnableAutoConfiguration注解

启用SpringBoot自动装配功能,尝试猜测和配置可能需要的组件Bean。
自动装配类通常是根据类路径和定义的Bean来应用的。例如,如果类路径上有tomcat-embedded.jar,那么可能需要一个TomcatServletWebServerFactory(除非已经定义了自己的Servlet WebServerFactory Bean)。
自动装配试图尽可能地智能化,并将随着开发者定义自己的配置而取消自动装配相冲突的配置。开发者可以使用exclude()排除不想使用的配置,也可以通过spring.autoconfig.exclude属性排除这些配置。自动装配总是在用户定义的Bean注册之后应用。
用@EnableAutoConfiguration注解标注的类所在包具有特定的意义,通常用作默认扫描的包。通常建议将@EnableAutoConfiguration(如果没有使用@SpringBootApplication注解)放在根包中,以便可以搜索所有子包和类。
自动装配类是普通的Spring @Configuration类,使用SpringFactoriesLoader机制定位。通常使用@Conditional方式装配,最常用的是@ConditionalOnClass和@ConditionalOnMissingBean注解。
  1. @AutoConfigurationPackage
  2. @Import(AutoConfigurationImportSelector.class)
  3. public @interface EnableAutoConfiguration {
  4.         /**
  5.          * Exclude specific auto-configuration classes such that they will never be applied.
  6.          */
  7.         Class<?>[] exclude() default {};
  8.         /**
  9.          * Exclude specific auto-configuration class names such that they will never be
  10.          * applied.
  11.          * 当类路径下没有指定的类时,可以使用这个属性指定排除的类
  12.          */
  13.         String[] excludeName() default {};
  14. }
复制代码
该注解Import了AutoConfigurationImportSelector类,AutoConfigurationImportSelector类实现了DeferredImportSelector接口。
Import注解和DeferredImportSelector接口在之前的"Spring @Import注解源码分析"中详细分析过,此处在介绍它们,只分析AutoConfigurationImportSelector的工作流程。
AutoConfigurationImportSelector类

DeferredImportSelector接口

A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional.
Implementations can also extend the org.springframework.core.Ordered interface or use the org.springframework.core.annotation.Order annotation to indicate a precedence against other DeferredImportSelectors.
Implementations may also provide an import group which can provide additional sorting and filtering logic across different selectors.
AutoConfigurationGroup类

AutoConfigurationImportSelector的getImportGroup方法返回了AutoConfigurationGroup类。
  1. private static class AutoConfigurationGroup implements
  2.                 DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
  3.         private final Map<String, AnnotationMetadata> entries = new LinkedHashMap<>();
  4.         private final List<AutoConfigurationEntry> autoConfigurationEntries = new ArrayList<>();
  5.         // ... 略
  6.         @Override
  7.         public void process(
  8.                         AnnotationMetadata annotationMetadata,
  9.                         DeferredImportSelector deferredImportSelector) {
  10.                 // AutoConfigurationEntry类使用List保存Configuration类
  11.                 AutoConfigurationEntry autoConfigurationEntry =
  12.             ((AutoConfigurationImportSelector) deferredImportSelector)
  13.                                 .getAutoConfigurationEntry(annotationMetadata);
  14.                 this.autoConfigurationEntries.add(autoConfigurationEntry);
  15.                 for (String importClassName : autoConfigurationEntry.getConfigurations()) {
  16.                         this.entries.putIfAbsent(importClassName, annotationMetadata);
  17.                 }
  18.         }
  19.         @Override
  20.         public Iterable<Entry> selectImports() {
  21.                 // 查找排除的配置类
  22.                 Set<String> allExclusions = this.autoConfigurationEntries.stream()
  23.                                 .map(AutoConfigurationEntry::getExclusions)
  24.                                 .flatMap(Collection::stream)
  25.                                 .collect(Collectors.toSet());
  26.                 // 所有配置类
  27.                 Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
  28.                                 .map(AutoConfigurationEntry::getConfigurations)
  29.                                 .flatMap(Collection::stream)
  30.                                 .collect(Collectors.toCollection(LinkedHashSet::new));
  31.                 // 将排除的配置类移除掉
  32.                 processedConfigurations.removeAll(allExclusions);
  33.                 // 排序
  34.                 return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
  35.                                 .map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
  36.                                 .collect(Collectors.toList());
  37.         }
  38.         // ... 略
  39. }
复制代码
从上面的代码可以看出,查找自动装配类的逻辑在getAutoConfigurationEntry方法中。
getAutoConfigurationEntry方法

从META-INF/spring.factories文件解析EnableAutoConfiguration配置。
META-INF/spring.factories文件示例:
  1. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
  2.         AnnotationAttributes attributes = getAttributes(annotationMetadata);
  3.         // 查找自动装配类
  4.         List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
  5.         // 以下几行为查找排除类、过滤等操作
  6.         configurations = removeDuplicates(configurations);
  7.         Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  8.         checkExcludedClasses(configurations, exclusions);
  9.         configurations.removeAll(exclusions);
  10.         // 这里的Filter是从META-INF/spring.factories文件解析出来的
  11.         configurations = getConfigurationClassFilter().filter(configurations);
  12.         // 触发事件
  13.         fireAutoConfigurationImportEvents(configurations, exclusions);
  14.         return new AutoConfigurationEntry(configurations, exclusions);
  15. }
  16. protected List<String> getCandidateConfigurations(
  17.                 AnnotationMetadata metadata, AnnotationAttributes attributes) {
  18.         // 从META-INF/spring.factories文件查找EnableAutoConfiguration配置
  19.         List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
  20.                                                 getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
  21.         return configurations;
  22. }
复制代码
SpringFactoriesLoader类loadFactoryNames方法

Load the fully qualified class names of factory implementations of the given type from "META-INF/spring.factories", using the given class loader.
  1. public static List<String> loadFactoryNames(Class<?> factoryType, ClassLoader classLoader) {
  2.         String factoryTypeName = factoryType.getName();
  3.         return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
  4. }
  5. private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
  6.         MultiValueMap<String, String> result = cache.get(classLoader);
  7.         if (result != null) {
  8.                 return result;
  9.         }
  10.         try {
  11.                 // FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
  12.                 // 从类路径下查找META-INF/spring.factories文件
  13.                 Enumeration<URL> urls = (classLoader != null ?
  14.                                 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
  15.                                 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
  16.                 result = new LinkedMultiValueMap<>();
  17.                 while (urls.hasMoreElements()) {
  18.                         URL url = urls.nextElement();
  19.                         UrlResource resource = new UrlResource(url);
  20.                         // 获取properties配置
  21.                         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
  22.                         for (Map.Entry<?, ?> entry : properties.entrySet()) {
  23.                                 String factoryTypeName = ((String) entry.getKey()).trim();
  24.                                 for (String factoryImplementationName :
  25.                      StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
  26.                                         result.add(factoryTypeName, factoryImplementationName.trim());
  27.                                 }
  28.                         }
  29.                 }
  30.                 // 把配置添加缓存
  31.                 cache.put(classLoader, result);
  32.                 return result;
  33.         } catch (IOException ex) {
  34.                 throw new IllegalArgumentException("Unable to load factories from location [" +
  35.                                 FACTORIES_RESOURCE_LOCATION + "]", ex);
  36.         }
  37. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

数据人与超自然意识

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表