SpringBoot自动配置原理

SpringBoot自动配置原理解析

SpringBoot是基于Spring的框架,SpringBoot尝试通过添加the jar dependencies的方式来自动化的为我们配置我们需要的各种组件,要想使用SpringBoot的自动配置,就必须添加 @EnableAutoConfiguration 或者 @SpringBootApplication 注解到@Configuration修饰的类上面

我们通常情况下构建的SpringBoot的主配置类都是如下形式:

@SpringBootApplication //自动配置的操作都在里面啦
public class MainApplication2 {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication2.class);
    }
}

要说自动配置的原理,当然是来到@SpringBootApplication注解

注解主要有以下三个注解合成:

@SpringBootConfiguration //声明当前类是一个配置类
@EnableAutoConfiguration //做两件事:1.确定包扫描路径 2.加载自动配置类
@ComponentScan(...)      //把指定路径下的组件加入容器

@SpringBootConfiguration里面主要是有@Configuration注解

@Configuration //作用是声明配置类
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

@ComponentScan注解的作用是把指定路径下的组件扫描进容器里面 :
SpringBoot 里面的这个注解使用如下文:主要是为了进行一些组件的过滤工作

@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

我们可以使用下面三个注解来代替@SpringBootApplication注解的作用

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.llm") 
public class MainApplication2 {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication2.class);
    }
}

上面的三个注解等价于:

@SpringBootApplication(scanBasePackages = {"com.llm"}) //scanBasePackages定义扫描的基础包

关于这个scanBasePackages属性,可以在@SpringBootApplication找到它的定义:

 @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
String[] scanBasePackages() default {};

可以看到这个属性等价于@ComponentScan注解的basePackages属性:

也就是说通过这个属性可以实现指定文件路径的扫描

最后的一个当然就是自动配置的核心:@EnableAutoConfiguration注解,使用这个注解完成自动配置的骚操作

@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {}; //定义需要排除的类的Class

    String[] excludeName() default {};
}

@AutoConfigurationPackage注解分析

里面主要有两个注解,首先是@AutoConfigurationPackage,这个注解主要来定义当前类为主配置类(就是SpringBootApplication修饰的类)并且把当前主配置类所属的包定义为扫描的基础包

首先来到这个注解里面,可以看到主要是导入了一个组件

@Import({Registrar.class})
public @interface AutoConfigurationPackage {
    String[] basePackages() default {}; //可以在`SpringBootApplication`注解里面定义basePackages属性,就是从这里来的
    Class<?>[] basePackageClasses() default {};
}

除了这个注解里面的属性可以用来定义扫描基础包外,注解类上面还有一个@Import({Registrar.class})来进行修饰

这个注解的作用是往容器中导入了一个类为Registrar的组件,关于@Import()注解的作用,读者可以自行百度

然后我们来看看这个Registrar类里面到底有何乾坤:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }
		//这个方法在Registrar被注入容器的时候被调用,主要完成基础包的导入
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
        }
    }

可以看到这个类继承了ImportBeanDefinitionRegistrar类,熟悉Spring的Bean生命周期的同学都应该知道,在容器初始化BeanDefination的时候会调用registerBeanDefinitions()方法,具体细节,读者可以自己百度

在上面的registerBeanDefinitions(...)方法上面打一个断点,观察里面的运行情况:

在这里插入图片描述

里面主要有下面这一行代码:

AutoConfigurationPackages.register(registry, (String[])(newAutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
  1. AutoConfigurationPackages.register(...)主要完成指定包下类向容器中的注册

  2. 主要和核心处理逻辑在newAutoConfigurationPackages.PackageImports(metadata)).getPackageNames()PackageImports是AutoConfigurationPackages的子类:

    • 首先来到PackageImports类中:

      private static final class PackageImports {
              //存储需要扫描的包的名字
              private final List<String> packageNames;
      		
              PackageImports(AnnotationMetadata metadata) {
                  //首先从@AutoConfigurationPackage注解中的两个属性中找找是否定义basePackages和basePackageClasses的属性是否已经定义
                  AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
                  List<String> packageNames = new ArrayList();
                  String[] var4 = attributes.getStringArray("basePackages");
                  int var5 = var4.length;
      
                  int var6;
                  for(var6 = 0; var6 < var5; ++var6) {
                      String basePackage = var4[var6];
                      packageNames.add(basePackage);
                  }
      
                  Class[] var8 = attributes.getClassArray("basePackageClasses");
                  var5 = var8.length;
      
                  for(var6 = 0; var6 < var5; ++var6) {
                      Class<?> basePackageClass = var8[var6];
                      packageNames.add(basePackageClass.getPackage().getName());
                  }
      			//如果注解里面的属性没有定义,就会进入到这个判断方法中,然后调用元注解里,得到主配置类下面的包名字
                  if (packageNames.isEmpty()) {
                      packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
                  }
      
                  this.packageNames = Collections.unmodifiableList(packageNames);
              }
      
              List<String> getPackageNames() {
                  return this.packageNames;
              }
         		 //....
      }
      

在这里插入图片描述

可以看到在这个类的构造方法中会完成属性packageNames的配置,如果没有在@SpringBootApplication中配置basePackages或者basePackageClasses的属性的话就会默认注入当前主配置类所在的包

 - 这个过程就完成了我们经常熟知的`SpringBoot`的属性:<kbd>默认扫描当前主配置类所属的包下面的所有的类</kbd>

@Import({AutoConfigurationImportSelector.class})注解

上面就完成了@AutoConfigurationPackage注解的分析,下面就是另外一个重要的注解,
@Import({AutoConfigurationImportSelector.class})粉墨登场啦!!!

可以看到,这也是通过@Import注解来向容器中注入AutoConfigurationImportSelector组件:

当然,我们需要进入到AutoConfigurationImportSelector类里面一探究竟:

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

    public AutoConfigurationImportSelector() {
    }
	
    //这个方法用来导入指定地址的自动配置类
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }
    
    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //getCandidateConfigurations(...)方法会从META-INF/spring.factories中加载所有的配置类
            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);
        }
    }
    //...

打断点,来到List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);里面
debug2这个方法里面的一行关键代码就是:

List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

这个loadFactoryNames方法调用了loadSpringFactories方法,在这个方法里面就有我们常见的文件位置啦!!!
debug3懂SpringBoot的朋友想必都不会对META-INF/spring.factories文件陌生,这个文件里面写好了有关我们开发所有的配置类的名字,在这些类的名字通常都以***AutoConfiguration结尾
debug4
写到这里,关于整个SpringBoot的大致配置流程,就走的差不多啦,在下实力有限,

肯定会有疏漏之处,若是前辈看到,若能指点一二,感激不尽。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值