什么是 SpringBoot 自动装配
SpringBoot 的自动配置是 SpringBoot 框架的核心特性之一。可以自动配置和加载 SpringBoot 所需的各种组件和功能,从而大大的减少开发人员手动配置的工作。
在传统的 Spring 应用程序中,我们需要手动配置各种组件,如数据源、Web 容器、事务管理器等。这些配置需要编写大量的 XML 配置文件或 Java 配置类,增加了开发的工作量和复杂性。而 SpringBoot 的自动装配通过约定优于配置的理念,根据项目的依赖和配置信息,自动进行配置,使得开发人员无需进行大量的手动配置。
用 Redis 举例来理解自动装配:
我们在项目中使用 Redis 时,往往有以下几步:
- 引入 Redis 依赖–starter
- 配置 Redis 服务器
- 可以直接使用 RedisTemplate Bean 对象了
我们用 Maven 引入了一个 starter ,其余的什么都没有做,但是 SpringBoot 自动完成了 Redis 的配置。
说白了就是:当 Spring 容器启动时,一些配置类、bean 对象就自动存入 IOC 容器中,不需要我们手动声明,从而简化了开发,省去了繁琐的配置操作。在 SpringBoot 应用里面只需要在启动类上加上 @SpringBootApplication 注解就可以实现自动装配。
Springboot 加载配置类的两大方式
-
使用 @ComponentScan
● 对指定的 package 进行扫描,找到其中符合条件的类,默认是搜索被注解 @Component 修饰的类。
● 通过属性 basePackages 或 basePackagesClassses,来指定要扫描的 package。
● 如果未指定 package,则默认扫描当前 @ComponentScan 所修饰的类所在的 package。 -
使用 @Import
@Import 提供了一种显示地从其他地方加载配置类的方式,其他地方通常指第三方的 jar 包。
● 导入普通类(这里的普通类是相对于随后两个接口而言的)
● 导入选择器 ImportSelector 的实现类。ImportSelector 接口有一个 selectorImport 方法,它的返回值是一个字符串数组,数组中的每个元素分别代表一个将被导入的配置类的全限定类名。利用该特性,我们可以给 IoC 容器动态的导入多个配置类。
● 导入注册器 ImportBeanDefinitionRegister 的实现类。通过这个接口我么可以将多个 BeanDefinition 注册到 IoC 容器中。
应当使用哪一个方式来加实现自动配置呢?
3. 使用 @ComponentScan 加载自动配置类很不方便,开发人员需要记住每一个第三方 jar 包的 package 名称,然后把他们写入应用程序中。
4. 使用 @Import 导入普通类比使用 @ComponentScan 还要麻烦,甚至需要第三方 jar 包中的具体类名。
5. 使用 @Import 导入 ImportBeanDefinitionRegister 也不合适,BeanDefinition 注册器的设计目标是对 @Bean 方法的补充,他是 BeanDefinition 层面的。
因此选用了@Import 导入选择器 ImportSelector 来实现自动配置!!!!
SpringBoot 自动装配流程
@SpringBootApplication 是一个复合注解,该注解主要是由三个注解组成:
- @SpringBootConfiguration:用来声明配置类
- @EnableAutoConfiguration:Springboot 实现自动化配置的核心注解
- @ComponentScan:组件扫描,只扫描 Springboot 项目包内的 Bean 并注册到 Spring 容器中
真正实现自动装配的是 @EnableAutoConfiguration注解。
@EnableAutoConfiguration 注解最终导入了 AutoConfigurationImportSelector.class 类:
@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 {};
}
如何实现 AutoConfigurationImportSelector ?
通过 SpringFactories 机制:
○ Java SPI 机制的延伸和扩展
○ Spring 框架的基础机制,在 Spring 以及 SpringBoot 源码中到处可见
○ 可以基于它来实现 SpringBoot 的自动配置功能
○ 它的核心逻辑是从 classpath 中读取到所有 Jar 包中的配置文件 META-IF/spring.factories 然后根据指定的 key 从配置文件中解析出对应的 value 值
AutoConfigurationImportSelector 通过 selectImports() 方法告诉 SpringBoot 都需要导入哪些组件。selectImports() 方法使用内部工具类 SpringFactoriesLoader 查找 classpath 上所用 jar 包中的 MATE-INF/spring.factories 进行加载, 实现将配置类信息交给 SpringFactory 加载器进行一系列的容器创建过程。
Java SPI 补充介绍: Java SPI 主要是应用于厂商自定义组件或插件中。在 java.util.ServiceLoader 的文档里有比较详细的介绍。简单的总结下 java SPI 机制的思想:我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块、xml 解析模块、jdbc 模块等方案。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。 Java SPI 就是提供这样的一个机制:为某个接口寻找服务实现的机制。
自动装配流程如下:
-
我们的启动类上有@SpringBootApplication
-
子注解 @SpringBootConfiguration,间接被 @Configuration 修饰,即启动类是一个源配置类
-
子注解 @ComponentScan,对源配置类所在的 Package 进行组件扫描
-
子注解 @EnableAutoConfiguration,最终导入一个 AutoConfigurationImportSelector 组件来实现自动配置
-
在这个 AutoConfigurationImportSelector 类内部会基于 selectImports() 方法使用内部工具类 SpringFactoriesLoader 的 loadFactoryNames() 方法查找 classpath 上所用 jar 包中的 MATE-INF/spring.factories 进行加载。
-
根据这些自动配置类上的条件注解 @Conditional 进行过滤,过滤出满足条件的自动配置类,然后通过 Spring 提供的 ImportSelector 接口来加载这些自动配置类,从而得到这些自动配置类的 BeanDefinition,最后根据 BeanDefinition 实例化对象。