[Spring Boot Starter系列]Spring Boot自动装配原理

目录

一、介绍

二、Spring Boot实现自动装配的简单流程(基于2.3.5.RELEASE版本)

三、总结


一、介绍

什么是Spring Boot的自动装配?

以下描述部分来自Spring Boot官网:

  • Spring Boot自动配置尝试根据您添加的jar依赖项自动配置您的Spring应用程序。
  • 您需要通过将@EnableAutoConfiguration或@SpringBootApplication注释添加到您的一个@Configuration类中来选择自动配置。您应该只添加一个@SpringBootApplication或@EnableAutoConfiguration注释。

补充:

  • 上诉说到Spring Boot会根据添加的jar依赖项自动配置您的Spring应用程序,具体的实现是通过扫描到引用的jar包中的META-INF/spring.factories文件,获取到你的配置类,文件内容大致如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.ricardo.cn.config.MyAutoConfigure

通过以上描述,个人理解为,自动装配就是能够为你创建你在jar包中定义的Bean,将其装配到容器中,供你使用你在jar包中实现的功能。

如果你想开发一个能够被Spring Boot自动装配所识别的Spring Boot Starter组件,那么请参考以下我的另外一篇博客,包含了开发的具体流程:

[自动装配系列]spring-boot-starter组件自定义开发及应用_明天再去学习的博客-CSDN博客

二、Spring Boot实现自动装配的简单流程(基于2.3.5.RELEASE版本)

1、通过上诉描述,Spring Boot想要实现自动装配,首先,你得需要在你的配置类中使用@SpringBootApplication或@EnableAutoConfiguration注解,毫无疑问,我们需要关注的只是@EnableAutoConfiguration注解,为什么这么说呢,请看@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 {}

可以看到,即使是@SpringBootApplication注解,其中也包含@EnableAutoConfiguration注解,所以@EnableAutoConfiguration注解是自动装配功能主入口。

2、@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 {};
}

官网中有这么一句话:

The @Import annotation can be used to import additional configuration classes

大致意思是说,@Import注解可以用于 导入其他配置类,所以,我们将关注点放在AutoConfigurationImportSelector.class上。

3、AutoConfigurationImportSelector.class的基础信息如下:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {}



public interface DeferredImportSelector extends ImportSelector {}



public interface ImportSelector {
}

当我定位到这个类之后,很疑惑,该把断点定位在何处,百度之后,有说是selectImport方法,有说是process方法,那到底是哪一个呢?很简单,两个方法都打一下断点。

3、将断点打在AutoConfigurationImportSelector.class的selectImport方法上。

@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

将断点打到此处后,启动debug模式,发现进不来,说明我这个版本的Spring Boot,selectImport方法不是主入口。

 

4、将断点打在AutoConfigurationImportSelector.class的process方法上。

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());
            });
            AutoConfigurationImportSelector.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);
            }

        }

将断点打到此处后,启动debug模式,发现进来了,说明我这个版本的Spring Boot,process方法是主入口。

在该方法中一步步debug走下去,查看参数内容:

当执行完getAutoConfigurationEntry方法时,可以看到返回结果如上图所示,都是AutoConfiguration结尾的配置(WhiteListAutoConfiguratio是我自定义的Spring Boot Starter组件,看到他之后,更加确定是getAutoConfigurationEntry方法了)。

5、给getAutoConfigurationEntry方法打断点。

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            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.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

在该方法中一步步debug走下去,查看参数内容:

 当执行完getCondidateConfigurations方法时,可以看到返回结果如上图所示,都是AutoConfiguration结尾的配置,可以确定自动装配与该方法有关。

6、查看getCondidateConfigurations方法。

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;
    }

通过该方法中英文描述可以看出,自动装配扫描的是META-INF/spring.factories文件。

7、通过上诉流程,进入到SpringFactoriesLoader.loadFactoryNames

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());
    }

进入到关键方法loadSpringFactories,一步步断点走下去,urls中包含了我们的所有外部jar包(通过debug查看到了urls内容),然后遍历每个jar包,扫描到我们jar包下的META-INF/spring.factories,获取到我们的配置类。

当调用完上诉步骤后,返回结果如下:

在步骤6中,又通过EnableAutoConfiguration.class从上诉集合中取到相关的value值列表。

8、至此,我们便拿到了需要自动装配的配置类,进行初始化。

三、总结

该文章主要是作为我以下的文章的支撑,只是作为学习参考,如果内容有错误的地方,可以进行留言提示,我看到的话会进行更正,谢谢:

[自动装配系列]spring-boot-starter组件自定义开发及应用_明天再去学习的博客-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值