Spring Boot自动装配原理

  • 前言
    • SpringBoot是一个脚手架,通常喜欢拿SpringBoot和Spring做比较,SpringBoot只是对于SSM框架开发时候的配置项做了一个默认配置,将开发中约定大于配置进行了实现,大大节约了开发时间写各种繁琐的配置文件环节,达到了开箱即用的便捷体验
  • SpringBoot入口
// 源码入口就是 @SpringBootApplication注解
@SpringBootApplication
public class BootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(BootDemoApplication.class, args);
    }
}


  • 查看@SpringBootApplication注解
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration  // 自动装配原理的核心注解
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  • ​查看​@EnableAutoConfiguration注解
@Inherited
@AutoConfigurationPackage 
/**
* 自动装配的时候使用的是@Import注解
* 作用是导入一个或者多个组件搭配@Configuration注解使用,交给spring容器管理
* 该注解有三种方式
* 1.@import:指定导入一个或者多个类
* 2.ImportSelector:定义一个类实现该接口,重写selectImports方法
* 3.
* 扩展:@ImportResource:指定导入一个或者多个xml文件
*/
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  • AutoConfigurationImportSelector类源码解析
// 重写来自于DeferredImportSelector.Group#process
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                 () -> String.format("Only %s implementations are supported, got %s",
                                     AutoConfigurationImportSelector.class.getSimpleName(),
                                     deferredImportSelector.getClass().getName()));
    // 获取自动装配信息
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
        .getAutoConfigurationEntry(annotationMetadata);
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

// 获取自动装配信息
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 加载所有在META-INF/spring.factories中配置的类 跳到①
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    // 经过condition注解过滤后的自动配置类
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}
// ① META-INF/spring.factories中读取所有的候选的自动配置类名称
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    // 通过SpringFactoriesLoader读取所有的候选的自动配置类名称
                                                                        // ② 加载配置候选的类
    List<String> 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;
}
// ③ 加载配置候选的类
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

// 所以如果想要将一个外部(第三方)的类交给spring容器管理,可以把这个类配置添加到META-INF/spring.factories文件中。
// 例如自定义starter场景启动器
  • 总结:SpringBoot的自动装配原理本质上就是读取项目中所有的META-INF/spring.factories的配置类信息


  • @Import之 ImportSelector/ImportBeanDefinitionRegistrar扩展
// ImportSelector (重要!SpringBoot底层使用这种方式,但是又有区别)
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 返回值String[]中值依旧是要导入类的全限定名,注意,返回值可以是空数组但是不能为null
        return new String[]{"com.fun.bootdemo.custome.Demo1"};
    }
}
/**
* SpringBoot底层使用的是:
* org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
* 该类实现的是DeferredImportSelector接口,该接口包含有额外功能:
* gruop 该分组可以用来进行排序和过滤时同时DeferredImportSelector是优先级比较低的,
* 需要等到其他bean注册完了。才注册该接口导入的bean。这个功能很好的结合了@Conditon等条件注解
* 实现了用户自定义配置覆盖默认配置
*/ 

// ImportBeanDefinitionRegistrar
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 指定注册bean定义信息
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Demo3.class);
        // demo3 是beanName
        registry.registerBeanDefinition("demo3", rootBeanDefinition);
    }
}
// 测试 (在配置类上)
@Import({MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})


上一篇:看了这篇,面试官问你APP体积优化再也不用WTF了


下一篇:DOS命令:列出某目录下的所有文本文件名并重定向到某文件