SpringBoot 系列之自动装配的原理源码分析

1. 概述

SpringBoot 的核心就是自动装配,他致力于“约定优于配置”的原则,它通过提供默认的配置来减少开发者的配置工作。这意味着,多数情况下,开发者可以快速启动一个Spring应用,而不需要进行复杂的配置。

2. 前置知识

SpringBoot 系列之前置知识

3. SpringApplication.run()方法

每个 SpringBoot 服务的启动入口都是SpringApplication.run(),那么它究竟是什么呢?我们来看代码
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面的代码,我们可以看到,已经到了 spring 源码层面了,而在 spring 源码中refresh()方法里面包含了创建容器、创建 bean、实例化 bean、初始化bean以及 postprocessor 等一系列的操作。
由此可知,springboot 中的SpringApplication.run方法底层调用的是 spring 的refresh()方法。

4. @SpringBootApplication注解

我们通过源码分析

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan( 
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
// ..........
}

@ComponentScan 前面我们介绍过了,是扫描路径的。
@SpringBootConfiguration注解实质上就是@Configuration注解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

所以重点就是@EnableAutoConfiguration注解了。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

由此可见使用了 Import 注解,我们继续看导入的类AutoConfigurationImportSelector

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }
	public Class<? extends DeferredImportSelector.Group> getImportGroup() {
        return AutoConfigurationGroup.class;
    }
}

实现了DeferredImportSelector接口,而DeferredImportSelector继承ImportSelector接口

分析至此,很多同学可能觉得,必是调用ImportSelector接口的selectImports方法完成的自动状态,其实不然,尽管AutoConfigurationImportSelector类确实有selectImports方法,但是,当我们 debug 的时候看到并没有进入selectImports方法,那么是在哪里调用的呢?别急,我们后面分析这块,这里留个标记!!!

public interface DeferredImportSelector extends ImportSelector {
    @Nullable
    default Class<? extends Group> getImportGroup() {
        return null;
    }

    public interface Group {
        void process(AnnotationMetadata metadata, DeferredImportSelector selector);

        Iterable<Entry> selectImports();

        public static class Entry {
            private final AnnotationMetadata metadata;
            private final String importClassName;

            public Entry(AnnotationMetadata metadata, String importClassName) {
                this.metadata = metadata;
                this.importClassName = importClassName;
            }

            public AnnotationMetadata getMetadata() {
                return this.metadata;
            }

            public String getImportClassName() {
                return this.importClassName;
            }

            public boolean equals(@Nullable Object other) {
                if (this == other) {
                    return true;
                } else if (other != null && this.getClass() == other.getClass()) {
                    Entry entry = (Entry)other;
                    return this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName);
                } else {
                    return false;
                }
            }

            public int hashCode() {
                return this.metadata.hashCode() * 31 + this.importClassName.hashCode();
            }

            public String toString() {
                return this.importClassName;
            }
        }
    }
}

好,我们继续往下面分析代码

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
        	// 获取元注解中的属性
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            // 从 所有spring.factories文件获取需要导入的类的全路径名
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            // 去掉重复项
            configurations = this.removeDuplicates(configurations);
            // 获取需要排除的导入项:就是SpringBootApplication注解的exclude和excludeName属性值
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            // 过滤,检查候选配置类上的注解@ConditionalOnClass,如果要求的类不存在,则这个候选类会被过滤不被加载
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    }

configurations = this.getConfigurationClassFilter().filter(configurations); 根据spring-autoconfiguration-metadata.properties文件中的配置判断要导入的 Configuration 是否满足条件,若不满足,则不导入,通过这种条件过滤可以有效的减少@configuration类的数量从而降低SpringBoot的启动时间。
在这里插入图片描述
我们重点分析下this.getCandidateConfigurations(annotationMetadata, attributes);语句

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        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<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            Map<String, List<String>> result = new HashMap();

            try {
            // 加载所有的配置文件资源
                Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");

                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()) {
                        Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        String[] var10 = factoryImplementationNames;
                        int var11 = factoryImplementationNames.length;

                        for(int var12 = 0; var12 < var11; ++var12) {
                            String factoryImplementationName = var10[var12];
                            ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                return new ArrayList();
                            })).add(factoryImplementationName.trim());
                        }
                    }
                }

                result.replaceAll((factoryType, implementations) -> {
                    return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                });
                cache.put(classLoader, result);
                return result;
            } catch (IOException var14) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
            }
        }
    }

现在我们梳理下上面遗留的问题:为什么没有走AutoConfigurationImportSelector类的selectImports方法?
问题的原因在于AutoConfigurationImportSelector实现的是DeferredImportSelector接口(Deferred:延迟),问题就出在这个接口上面,接下来我们看一下源码:
上面我们分析到了spring 中的代码 refresh 方法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

以上只是将所有的延迟加载导入类添加到 list 中了,那么在哪里处理的呢?
上面的步骤往回退一退,我们可以看到
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
然后再看processGroupImports方法。
在这里插入图片描述
在这里插入图片描述
然后我们就回到了DeferredImportSelector接口中
在这里插入图片描述
进而我们回到最原始的AutoConfigurationImportSelector类,这个类中有个内部类AutoConfigurationGroup实现了DeferredImportSelector.Group接口

private static class AutoConfigurationGroup implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
        //。。。。。。。。。。

        public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
            Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
                return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
            });
            // 在这里调用的getAutoConfigurationEntry方法,正式进入自动装配逻辑
            AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
            this.autoConfigurationEntries.add(autoConfigurationEntry);
            Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();

            while(var4.hasNext()) {
                String importClassName = (String)var4.next();
                this.entries.putIfAbsent(importClassName, annotationMetadata);
            }

        }

        public Iterable<DeferredImportSelector.Group.Entry> selectImports() {
            if (this.autoConfigurationEntries.isEmpty()) {
                return Collections.emptyList();
            } else {
                Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
                Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
                processedConfigurations.removeAll(allExclusions);
                return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {
                    return new DeferredImportSelector.Group.Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
                }).collect(Collectors.toList());
            }
        }

    }
  • 12
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值