这个问题可以从三个问题来回答:
- 什么是SpringBoot自动装配?
- SpringBoot是如何实现自动装配的?如何实现按需加载的?
- 如何实现一个Starter?
前言
没有SpringBoot的时候,也就是使用原生Spring时,我们需要使用XML配置,比较繁琐。即使Spring后面引入了基于注解的配置,我们在开启某些Spring特性或者引入第三方依赖的时候,还是需要使用XML或者Java进行显示配置。
在有了SpringBoot之后呢,我们只需要添加相关的依赖,无需配置,添加@SpringBootApplication
注解即可。并且还可以通过修改SpringBoot的全局配置文件application.properties
或者application.yml
即可对项目进行自定义配置,比如修改启动端口号、配置数据库信息等。
那么为什么SpringBoot能这么干呢?
这就得益于自动装配机制了,可以说,自动装配是SpringBoot的核心。
什么是SpringBoot的自动装配?
Spring Boot 自动装配是基于 Java 的 SPI(Service Provider Interface) 和 Spring Framework 的条件注解 Conditional On Class 等功能实现的。
SpringBoot定义了一套接口规范,即SpringBoot在启动是会扫描外部引用Jar包中的
META-INF/spring.factories
文件,将文件中配置的类型信息加载到Spring容器,并执行类中定义的各种操作。对于外部Jar来说,只需要按照SpringBoot定义的标准,就能将自己的功能装配进SpringBoot。
在创建 Spring Boot 项目时,我们会引入很多 Starter,比如 Web, Data
JPA, Security 等,它们都包含了一些已经为我们写好并默认配置好的组件,如
Tomcat, Hibernate, Spring Security 等等。当我们引入这些 starter
之后,Spring Boot 就能根据 starter 中
META-INF/spring.factories
配置的值,但是不一定会全部生效,需要判断条件是否成立(比如:RabbitAutoConfiguration
类上有注解@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
,意味着要导入括号内的两个类才会装配RabbitAutoConfiguration
),只要导入了对应的Starter,就会有对应启动器,有了启动器,就可以自动装载对应的
Bean 到容器中。
SpringBoot是如何实现自动装配的?
先来看SpringBoot的核心注解@SpringBootApplication
|
可以将@SpringBootApplication
注解看作是@SpringBootConfiguration
、@EnableAutoConfiguration
、@ComponentScan
注解的组合,这三个注解的作用是:
@SpringBootConfiguration
:该注解实际上等价于@Configuration
,允许在上下文中注册额外的Bean或者导入其他配置类@EnableAutoConfiguration
:开启SpringBoot的自动配置机制@ComponentScan
:扫描被@Component
注解的Bean,该注解默认会扫描启动类所在包下的所有的类,可以通过属性basePackages
指定扫描包,也可以自定义不扫描某些Bean
很显然,其中的@EnableAutoConfiguration
注解是实现自动装配的重要注解,弄他!
@EnableAutoConfiguration:实现自动装配的核心注解
|
@AutoConfigurationPackage
:作用使用main包下的所有组件注册进容器中@Import
:加载自动装配类,形如 xxxAutoConfiguration
那么就很显然了,该注解引入的AutoConfigurationImportSelector
类是个核心类
AutoConfigurationImportSelector:加载自动装配类
继承体系:
public interface ImportSelector { |
AutoConfigurationImportSelector
类实现了ImportSelector
接口,也就实现了该接口中的selectImports()
方法,该方法主要用于获取所有符合条件的类的全限定名,这些类需要被加载IOC容器中。
AutoConfigurationImportSelector#selectImports()
private static final String[] NO_IMPORTS = {}; |
此处呢,需要重点关注getAutoConfigurationEntry()
方法,这个方法主要负责加载自动装配类。
方法追踪链:
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
/** |
第一步:判断自动装配开关是否开启,默认为spring.boot.enableautoconfiguration=true
,可以在application.properties
中配置
第二步:获取@EnableAutoConfiguration
注解中配置的exclude
和excludeName
属性
第三步:获取需要自动装配的所有配置类,读取META-INF/spring.factories
文件,所有
Spring Boot Starter
下的META-INF/spring.factories
都会被读取到
第四步:筛选配置,@ConditionalOnXXX
中的所有条件都满足,该类才会生效,才会装配进IOC容器
比如:
|
SpringBoot提供的条件注解:
@ConditionalOnBean
:当容器里有指定 Bean 的条件下@ConditionalOnMissingBean
:当容器里没有指定 Bean 的情况下@ConditionalOnSingleCandidate
:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean@ConditionalOnClass
:当类路径下有指定类的条件下@ConditionalOnMissingClass
:当类路径下没有指定类的条件下@ConditionalOnProperty
:指定的属性是否有指定的值@ConditionalOnResource
:类路径是否有指定的值@ConditionalOnNotWebApplication
:当前项目不是 Web 项目的条件下@ConditionalOnWebApplication
:当前项目是 Web 项目的条件下
如何开发一个Starter
为什么需要Starter?
理想情况:开发者只需要关心调用那些接口、传递哪些参数,就跟调用自己写的代码一样简单。
开发Starter的好处:开发者引入Starter之后,可以直接在application.yml
中写配置,自动创建客户端。
进一步说明
为了方便开发者的调用,我们不能让他们每次都自己编写签名算法,这显然很繁琐。因此,我们需要开发一个简单易用的 SDK,使开发者只需关注调用哪些接口、传递哪些参数,就像调用自己编写的代码一样简单。实际上,RPC(远程过程调用)就是为了实现这一目的而设计的。RPC它就是追求简单化调用的理想情况。类似的例子是小程序开发或者调用第三方 API,如腾讯云的 API,它们都提供了相应的 SDK。
如何开发Starter?
- 确认所需要的依赖,需要引入
spring-boot-configuration-processor
(配置文件编写提示) - 过程代码编写
- 配置类编写
- 在
resources
目录下创建META-INF/spring.factories
,其中配置自动配置类org.springframework.boot.autoconfigure.EnableAutoConfiguration=xxx
最后就是打包安装发布,供其他项目使用。
总结
SpringBoot通过@EnableAutoConfiguration
注解开启自动装配,通过SpringFactoriesLoader加载META-INF/spring.factories
中的自动配置类实现自动装配,自动装配类其实就是通过@Conditional
按需加载配置类,想要期望的类被装配就需要引入相关的依赖包。