SpringBoot⾃动装配流程
Spring Boot ⾃动装配流程
本⽂以 mybatis-spring-boot-starter 为例简单分析 Spring Boot 的⾃动装配流程。
Spring Boot 发现⾃动配置类
这⾥说的⾃动配置类指的是在 META-INF/spring.factories ⽂件中声明的 XXXAutoConfiguration 类。
⾸先,我们从 @SpringBootApplication 注解的定义中,我们可以发现有⼀个叫做 @EnableAutoConfiguration 的注解,这也是 SpringBoot 实现⾃动装配最关键的注解。
//@EnableAutoConfiguration 注解的定义
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "ableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@Target @Retention @Documented @Inherited 都是 jdk 提供的注解,有兴趣可以去查查看,这⾥就不做分析了。这⾥主要分
析 @AutoConfigurationPackage 和 @Import({AutoConfigurationImportSelector.class}) 究竟起到什么作⽤。
//@AutoConfigurationPackage 注解的定义
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
在 @AutoConfigurationPackage 注解的定义中,我们⼜发现⼀个 @Import 注解。 @Import 注解是由 Spring 提供的,作⽤是将某个类实例化并加⼊到 Spring IoC 容器中。所以我们要想知
道 @Import({Registrar.class}) 究竟做了什么就需要了解 Registrar 这个类⾥包含了哪些⽅法。
//Registrar 类的定义
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
Registrar 类⾥⼀共有两个⽅法,分别是 determineImports 和 registerBeanDefinitions 。
determineImports ⽅法在我的项⽬的启动过程中并没有触发断点,官⽅的⽂档描述这个⽅法返回的是⼀组代表要导⼊项的对象。
registerBeanDefinitions ⽅法触发断点后发现
new AutoConfigurationPackages.PackageImport(metadata)).getPackageName() ⽅法返回的就是 @SpringBootApplication 注解所在的类的包名。
所以 @AutoConfigurationPackage 注解的作⽤应该是扫描与 @SpringBootApplication 标注的类同⼀包下的所有组件。
了解了 @AutoConfigurationPackage 注解后,我们回到 @EnableAutoConfiguration 的定义,还有⼀个 @Import({AutoConfigurationImportSelector.class}) 需要我们了解。
AutoConfigurationImportSelector 类定义的内容很多,我们着重了解其中⼀个重要的⽅法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = AutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
Configurations());
}
}
这个⽅法中,除了 loadMetadata 获取注解元数据之外,就是 getAutoConfigurationEntry 获取⾃动配置条⽬。
//getAutoConfigurationEntry 的定义
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoC
onfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {    if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = Attributes(annotationMetadata);
List<String> configurations = CandidateConfigurations(annotationMetadata, attributes);
configurations = veDuplicates(configurations);
Set<String> exclusions = Exclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
然后我们再进⼊到这个叫做 getCandidateConfigurations 的⽅法中,这个⽅法名告诉我们这个⽅法的作⽤是获取候选配置。
//getCandidateConfigurations 的定义
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.SpringFactoriesLoaderFactoryClass(), BeanClassLoader());
return configurations;
}
从这个⽅法中的 Empty() 中我们可以反推得出,Spring Boot 除了扫描⾃⼰ jar 包中 META-INF/spring.factories 之外,还会去别的 jar 包中是否存在 META-INF/spring.factories 。这也为第三⽅开发⾃⼰的 spring-boot-starter 提供了便利。
@Conditional 系列注解
在研究 mybatis-spring-boot-starter 之前,我们还需要了解⼀下 Spring 为提供的强⼤的 @Conditional 系列注解。
spring ioc注解@Conditional扩展注解作⽤(判断当前条件是否满⾜)
@ConditionalOnJava系统的java版本是否符合要求
@ConditionalOnBean容器中是否存在指定的Bean
@ConditionalOnMissingBean容器中不存在指定的类
@ConditionalOnExpression满⾜SpEL表达式指定规范
@ConditionalOnClass在系统中有指定的对应的类
@ConditionalOnMissingClass在系统中没有指定对应的类
@ConditionalOnSingleCandidate容器中是否指定⼀个单实例的Bean,或者个是⼀个⾸选的Bean
@ConditionalOnProperty系统中指定的对应的属性是否有对应的值
@ConditionalOnResource类路径下是否存在指定的资源⽂件
@ConditionalOnWebApplication当前是Web环境
@ConditionalOnNotWebApplication当前不是Web环境
@ConditionalOnJndi JNDI存在指定项
表格中的系统指的是项⽬本⾝,⽽⾮操作系统。
mybatis-spring-boot-starter
在 mybatis-spring-boot-starter 中,我们可以看到内容很少,仅有⼀个 l ⽂件⽤于引⼊依赖,所以 mybatis-spring-boot-starter 并不直接起作⽤,⽽是利⽤其它依赖完成⾃动配置。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId&batis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId&batis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId&batis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
</dependencies>
我们可以发现 mybatis-spring-boot-starter 的依赖项中有⼀个叫做 mybatis-spring-boot-autoconfigure 的依赖项。这很有可能就是 mybatis 对⾃⼰完成⾃动装配真正起作⽤的⼯具。
果然在 mybatis-spring-boot-autoconfigure 的 META-INF 中我们到了 spring.factories ⽂件。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
MybatisLanguageDriverAutoConfiguration 类是对各种语⾔的⽀持,如 Thymeleaf 、 FreeMarker 等。配置 Mybatis 核⼼组件的是 MybatisAutoConfiguration 类。
//MybatisAutoConfiguration 类定义
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class,
MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
//....
}
⾸先 MybatisAutoConfiguration 类是⼀个被 @Configuration 标记了的配置类。这不难理解, MybatisAutoConfiguration 类会为 Mybatis 配置⼀些关键的 Bean 并加⼊到容器中去。
接着就是两个 @Conditional 系列的注解,表⽰当项⽬中存在 SqlSessionFactory.class 与 SqlSessionFactoryBean.class 并且存在 DataSource.class 的单例实例或者⾸选实例
时, MybatisAutoConfiguration 才会被加⼊到容器中去。
@EnableConfigurationProperties({MybatisProperties.class}) 这个注解的作⽤是将配置⽂件( .propertyies 和 .yml)与 MybatisProperties 类关联起来,也就是说这个注解能让 Spring Boot 从配置⽂件中读取数据。
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class}) 这个注解的作⽤也⾮常明显,就是要求 Spring Boot 在装
配 MybatisAutoConfiguration 之前先完成 DataSourceAutoConfiguration 和 MybatisLanguageDriverAutoConfiguration 的装配。这样可以保证 Mybatis 在装配时,所有的依赖项都已经到位。
除了 MybatisAutoConfiguration 本⾝之外,类中也定义了⼀些按条件⽣成的 Bean,保证 Mybatis 能在各种条件下成功的⾃动装配。
总结
1. Spring Boot 在启动时除了扫描与启动类同⼀包下的组件之外,还会检查各个 jar 包中是否存在 META-INF/spring.factories ⽂件,为⾃动装配做准备。
2. 第三⽅的 spring-boot-starter 会通过将⾃⼰的⾃动装配类写到 META-INF/spring.factories 中让 Spring Boot 加载到容器中,使⾃动装配类能够⽣效。
3. 第三⽅的⾃动装配类会通过利⽤ @Conditional 系列注释保证⾃⼰能在各种环境中成功⾃动装配。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。