面试题:SpringBoot 的自动装配是怎样实现的?

一、SpringBoot 的自动装配实现:

自动装配的入口是复合注解 @SpringBootApplication ,它主要是由以下三个注解组成:

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

装配步骤如下:

  • @SpringBootConfiguration 里面是封装了一个@Configuration,作用就是将配置这个注解的类加载到 IOC 容器里面,主要是替换到 xml 配置文件
  • @ComponentScan 他的作用就是扫描指定的路径下需要装配的类, 自动装配到 spring 的 IOC 容器里面,标识需要装配的类的形式主要是 @Component@Repository@Service@Controller 这一类注解的标识,@ComponentScan 默认会扫描当前类名所在包名下面所有的加了相关标识注解载入 IOC 容器里面
  • @EnableAutoConfiguration 里面包含 @Import(AutoConfigurationImportSelector.class)
    • @Impor 引入实现了 ImportSelector 接口的AutoConfigurationImportSelector类进行动态注入
    • AutoConfigurationImportSelector 类里面的 selectImport主要是扫描MATE-INF下面的spring-autoconfiguration-matedata.propertiesspring.factories文件最后进行合并, 判断那些组件需要注入,进行动态条件生成, 判断那些组件不需要注入容器

二、从 SpringBootApplication 注解开始分析自动装配

打开 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 本质上是由 3 个注解组成,分别是

  1. @Configuration
  2. @EnableAutoConfiguration
  3. @ComponentScan

我们可以直接用这三个注解也可以启动 springboot 应用, 只是每次配置三个注解比较繁琐,所以直接用一个复合注解更方便些。 然后仔细观察者三个注解,除了 EnableAutoConfiguration 可能稍微陌生一点,其他两个注解使用得都很多

三、简单分析@Configuration

Configuration 这个注解大家应该有用过,它是 JavaConfig 形式的基于 Spring IOC 容器的配置类使用的一种注解。因 为 SpringBoot 本质上就是一个 spring 应用,所以通过这 个注解来加载 IOC 容器的配置是很正常的。所以在启动类里面标注了 @Configuration,意味着它其实也是一个 IOC容器的配置类。

从 Spring3 开始,Spring 就支持了两种 bean 的配置方式:

  • 一种是基于 xml 文件方式
  • 另一种就是 JavaConfig 任何一个标注了@Configuration 的 Java 类定义都是一个 JavaConfig 配置类。而在这个配置类中,任何标注了 @Bean 的方法,它的返回值都会作为 Bean 定义注册到 Spring 的 IOC 容器,方法名默认成为这个 bean 的 id

四、简单分析 ComponentScan

ComponentScan 这个注解是大家接触得最多的了,相当 于 xml 配置文件中的。 它的主要作用就是扫描指定路径下的标识了需要装配的类,自动装配到 spring 的 IOC容器中。

标识需 要装配的类的 形式主要是: @Component 、 @Repository、@Service、@Controller 这类的注解标识的类。

ComponentScan 默认会扫描当前 package 下的的所有加 了相关注解标识的类到 IOC 容器中;

五、简单分析 EnableAutoConfiguration

是在 Spring 3.1 版本中,提供了一系列的@Enable 开头的注解,

Enable 注解应该是在 JavaConfig 框架上更进一 步的完善,是的用户在使用 Spring 相关的框架是,避免配 置大量的代码从而降低使用的难度

比如常见的一些 Enable 注解:

  • @EnableWebMvc:这个注 解引入了 MVC 框架在 Spring 应用中需要用到的所有 bean
  • @EnableScheduling:开启计划任务的支持;

找到 EnableAutoConfiguration,我们可以看到每一个涉及 到 Enable 开头的注解,都会带有一个@Import 的注解。

@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    //...
}

5.1 Import 注解

import 就是把多个分来的容器配置合并在一个配置中。在 JavaConfig 中所表达的意义是一样的。

联想到 xml 形式下有一个<import resource/>形式的注解,就明白它的作用了。

5.2 深入分析 EnableAutoConfiguration

EnableAutoConfiguration 的 主 要 作 用 其 实 就 是 帮 助 springboot 应用把所有符合条件的@Configuration 配置 都加载到当前 SpringBoot 创建并使用的 IoC 容器中。

再回到 EnableAutoConfiguration 这个注解中,我们发现 它的 import 是这样:

@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    //...
}
5.2.1 AutoConfigurationImportSelector 是什么?

Enable注解不仅仅可以像前面演示的案例一样很简单的实 现多个 Configuration 的整合,还可以实现一些复杂的场景,比如可以根据上下文来激活不同类型的 bean, @Import 注解可以配置三种不同的 class

  • 第一种基于普通 bean 或者带有 @Configuration 的 bean 进行注入
  • 实现 ImportSelector 接口进行动态注入
  • 实现 ImportBeanDefinitionRegistrar 接口进行动态注入
5.2.2 @EnableAutoConfiguration 注解的实现原理

了解了 ImportSelector 和 ImportBeanDefinitionRegistrar 后,对于 EnableAutoConfiguration 的理解就容易一些了

它会通过 import 导入第三方提供的 bean 的配置类:AutoConfigurationImportSelector

@Import({AutoConfigurationImportSelector.class})

从名字来看,可以猜到它是基于 ImportSelector 来实现 基于动态 bean 的加载功能。

首先我们要知道 Springboot @Enable*注解的工作原理 ImportSelector 接口 selectImports 返回的数组(类的全类名)都会被纳入到 spring 容器中。

那么可以猜想到这里的实现原理也一定是一样的,定位到 AutoConfigurationImportSelector 这个类中的 selectImports 方法

//动态注入bean的方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        //扫描 spring-autoconfiguration-metadata.properties 文件,会结合前面的元数据进行过滤
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        //获取所有的动态注入的bean
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
        //返回beanName的String[]
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

本质上来说,其实 EnableAutoConfiguration 会帮助 springboot 应用把所有符合@Configuration 配置都加载 到当前 SpringBoot 创建的 IOC容器,而这里面借助了 Spring 框架提供的一个工具类 SpringFactoriesLoader 的 支持。以及用到了 Spring 提供的条件注解 @Conditional,选择性的针对需要加载的 bean 进行条件过滤

5.2.3 SpringFactoriesLoader

SpringFactoriesLoader 这个工具类它其实和 java 中的 SPI 机制的原理是一样的,不过它比 SPI 更好的 点在于不会一次性加载所有的类,而是根据 key 进行加载。

首先, SpringFactoriesLoader 的作用是从 classpath/META-INF/spring.factories 文件中,根据 key 来 加载对应的类到 Spring IOC容器中。

5.2.4 深入理解条件过滤

在分析 AutoConfigurationImportSelector 的源码时,会 先扫描spring-autoconfiguration-metadata.properties文件,最后在扫描 spring.factories 对应的类时,会结合前面的元数据进行过滤

为什么要过滤呢?

原因是很多的 @Configuration 其实是依托于其他的框架来加载的, 如果当前的 classpath 环境下没有相关联的依赖,则意味着这些类没必要进行加载,所以,通过这种条件过滤可以有效的减少 @configuration 类的数量从而降低 SpringBoot 的启动时间。

5.2.5 Conditional 中的其他注解
Conditions描述
@ConditionalOnBean在存在某个 bean 的时候
@ConditionalOnMissingBean不存在某个 bean 的时候
@ConditionalOnClass当前 classpath 可以找到某个类型的类时
@ConditionalOnMissingClass当前 classpath 不可以找到某个类型的类时
@ConditionalOnResource当前 classpath 是否存在某个资源文件
@ConditionalOnProperty当前 jvm 是否包含某个系统属性为某个值
@ConditionalOnWebApplication当前 spring context 是否是 web 应用程序

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值