SpringBoot底层原理——自动装配

1、引导加载自动配置类

MainApplication:启动类

@SpringBootApplication
public class MainApplication

@SpringBootApplication:是一个合成组件

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication 
1.1、@SpringBootConfiguration

它由@Configuration标注,也就是说,它本质上是一个配置类,而与其他类不同的是,@SpringBootConfiguration表示当前类是核心配置类

1.2、@ComponentScan

指定要扫描哪些包,它有一些默认属性

1.3、@EnableAutoConfiguration(最重要)
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

1.@AutoConfigurationPackage

自动配置包

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}
// 给容器中导入组件
// 将指定包下的所有组件导入进去

导入的组件是一个Registrar

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

   @Override
   public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
       // 批量注册
       // 通过注解元信息metadata获取到包名PackageImports.getPackageNames(),把包名封装到一个数组中toArray(new String[0]),然后注册register()进去
      register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
   }

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

}

2.@Import(AutoConfigurationImportSelector.class)

利用getAutoConfigurationEntry(annotationMetadata)方法给容器中批量导入组件

// 给容器中批量导入组件
getAutoConfigurationEntry(annotationMetadata);
// 调用getCandidateConfigurations()获取所有需要导入到容器的配置类,返回一个list
getCandidateConfigurations(annotationMetadata, attributes);
// 利用工厂加载SpringFactoriesLoader.loadFactoryNames()获取所有组件,返回一个map
SpringFactoriesLoader.loadFactoryNames();
// 从META-INF/spring.facotries位置,加载得到所有组件,默认扫描我们当前系统里所有从META-INF/spring.facotries位置的文件,以map类型返回
loadSpringFactories();
// spring-boot-autoconfigure-2.5.3.jar包里也有META-INF/spring.facotries
// 文件里写死了SpringBoot已启动就会加载的所有配置类

加载的自动配置类有134个

在这里插入图片描述

这些自动配置类都写死在spring-boot-autoconfigure-2.5.3.jar包中的META-INF/spring.facotries

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.autoconfigure.integration.IntegrationPropertiesEnvironmentPostProcessor

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
...

2、按需开启自动配置项

在这里插入图片描述

虽然,一股脑加载了所有配置类,但得益于SpringBoot的条件装配规则,最终会按需配置

3、修改默认配置

SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先

4、自动装配流程

xxxAutoConfiguration—>组件—>xxxProperties里面去拿值---->application.properties

总结:

  1. SpringBoot先加载所有自动配置类,xxxAutoConfiguration
  2. 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值,xxxProperties里面拿。xxxProperties和配置文件进行了绑定
  3. 生效的配置类就会给容器中装配很多组件
  4. 只要容器中有这些组件,相当于这些功能就有了
  5. 定制化配置
    • 用户直接用@Bean替换底层的组件
    • 用户求改配置文件的值

5、starter的工作原理

问题

如果我们想引用一个组件,步骤是:导入jar包、配置bean、使用@Autowired注解,但是一旦jar包发生版本更新,可能会导致配置的bean也要发生改变,这就会降低我们使用这个jar包的意愿

SpringBoot的解决方案

利用starter,我们省去配置bean的步骤,而是直接配置到starter里面,就可以直接用@Autowired进行使用

starter的底层原理

SpringBoot启动包:spring-boot-starter

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

为什么可以不写版本号?

pom.xml

继承自spring-boot-starter-parent

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.4</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

spring-boot-starter-parent

继承自spring-boot-dependencies

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.5.4</version>
</parent>

spring-boot-dependencies

<properties>
    <activemq.version>5.16.3</activemq.version>
    ...
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-amqp</artifactId>
            <version>${activemq.version}</version>
        </dependency>
        ...
    </dependencies>
</dependencyManagement>

在spring-boot-dependencies里面,定义了核心依赖包的版本号

那为什么不直接把spring-boot-dependencies作为parent放到工程里去呢?

因为有时我们需要自己配置一些组件,而不是用SpringBoot默认配好的组件,这时需要加载资源文件:yml、yaml、properties,所以要将这一步与导入核心依赖分开

spring-boot-starter-parent

<resource>
    <directory>${basedir}/src/main/resources</directory>
    <filtering>true</filtering>
    <includes>
        <include>**/application*.yml</include>
        <include>**/application*.yaml</include>
        <include>**/application*.properties</include>
    </includes>
</resource>

总结:

  • spring-boot-dependencies:定义版本号
  • spring-boot-starter-parent:加载资源文件yml、yaml、properties
  • spring-boot-starter:导入我们要用的包
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值