Spring Boot 自动装配流程

Spring Boot 自动装配流程

本文以 mybatis-spring-boot-starter 为例简单分析 Spring Boot 的自动装配流程。java

Spring Boot 发现自动配置类

这里说的自动配置类指的是在META-INF/spring.factories文件中声明的XXXAutoConfiguration类。spring

首先,咱们从@SpringBootApplication注解的定义中,咱们能够发现有一个叫作@EnableAutoConfiguration的注解,这也是 SpringBoot 实现自动装配最关键的注解。mybatis

//@EnableAutoConfiguration 注解的定义
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {spring-boot

String ENABLED\_OVERRIDE\_PROPERTY = "spring.boot.enableautoconfiguration";

Class<?>\[\] exclude() default {};

String\[\] excludeName() default {};

}工具

@Target`@Retention@Documented@Inherited都是 jdk 提供的注解,有兴趣能够去查查看,这里就不作分析了。这里主要分析@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})`究竟起到什么做用。this

//@AutoConfigurationPackage 注解的定义
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}spa

@AutoConfigurationPackage注解的定义中,咱们又发现一个@Import注解。@Import注解是由 Spring 提供的,做用是将某个类实例化并加入到 Spring IoC 容器中。因此咱们要想知道@Import({Registrar.class})究竟作了什么就须要了解Registrar这个类里包含了哪些方法。操作系统

//Registrar 类的定义
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {3d

Registrar() {
}

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}

public Set<Object> determineImports(AnnotationMetadata metadata) {
    return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}

}code

Registrar类里一共有两个方法,分别是determineImportsregisterBeanDefinitions

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 = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

}

这个方法中,除了loadMetadata获取注解元数据以外,就是getAutoConfigurationEntry获取自动配置条目。

//getAutoConfigurationEntry 的定义
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {

if (!this.isEnabled(annotationMetadata)) {
    return EMPTY\_ENTRY;
} else {
    AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
    List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
    configurations = this.removeDuplicates(configurations);
    Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
    this.checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(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.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;

}

从这个方法中的Assert.notEmpty()中咱们能够反推得出,Spring Boot 除了扫描本身 jar 包中META-INF/spring.factories以外,还会去找别的 jar 包中是否存在META-INF/spring.factories。这也为第三方开发本身的spring-boot-starter提供了便利。

@Conditional 系列注解

在研究mybatis-spring-boot-starter以前,咱们还须要了解一下 Spring 为提供的强大的@Conditional系列注解。

@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中,咱们能够看到内容不多,仅有一个pom.xml文件用于引入依赖,因此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\>org.mybatis.spring.boot</groupId\>
    <artifactId\>mybatis-spring-boot-autoconfigure</artifactId\>
</dependency\>
<dependency\>
    <groupId\>org.mybatis</groupId\>
    <artifactId\>mybatis</artifactId\>
</dependency\>
<dependency\>
    <groupId\>org.mybatis</groupId\>
    <artifactId\>mybatis-spring</artifactId\>
</dependency\>

</dependencies>

咱们能够发现mybatis-spring-boot-starter的依赖项中有一个叫作mybatis-spring-boot-autoconfigure的依赖项。这颇有可能就是 mybatis 对本身完成自动装配真正起做用的工具。

果真在mybatis-spring-boot-autoconfigureMETA-INF中咱们找到了spring.factories文件。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

MybatisLanguageDriverAutoConfiguration类是对各类语言的支持,如ThymeleafFreeMarker等。配置 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.classSqlSessionFactoryBean.class而且存在DataSource.class的单例实例或者首选实例时,MybatisAutoConfiguration才会被加入到容器中去。

@EnableConfigurationProperties({MybatisProperties.class})这个注解的做用是将配置文件( .propertyies 和 .yml)与MybatisProperties类关联起来,也就是说这个注解能让 Spring Boot 从配置文件中读取数据。

@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})这个注解的做用也很是明显,就是要求 Spring Boot 在装配MybatisAutoConfiguration以前先完成DataSourceAutoConfigurationMybatisLanguageDriverAutoConfiguration的装配。这样能够保证 Mybatis 在装配时,全部的依赖项都已经到位。

除了MybatisAutoConfiguration自己以外,类中也定义了一些按条件生成的 Bean,保证 Mybatis 能在各类条件下成功的自动装配。

总结

  1. Spring Boot 在启动时除了扫描与启动类同一包下的组件以外,还会检查各个 jar 包中是否存在META-INF/spring.factories文件,为自动装配作准备。
  2. 第三方的spring-boot-starter会经过将本身的自动装配类写到META-INF/spring.factories中让 Spring Boot 加载到容器中,使自动装配类可以生效。
  3. 第三方的自动装配类会经过利用@Conditional系列注释保证本身能在各类环境中成功自动装配。
相关文章
相关标签/搜索