Part 1

自发布以来,Spring Boot取得巨大成功:通过约定胜于配置的理念,提高开发人员的生产力。然而,有时,感觉太神奇。就是因为这个原因,我一直反对自动装配。当某事不生效时,很难回到正轨。

这就是为什么我想深入了解 Spring Boot starter 机制 - 理解每一个角角落落。 这篇文章是第一部分,将重点分析工作原理。 第二部分将研究创建 starter。

spring.factories

每个 Spring Boot starter 的根源都是建立在 META-INF/spring.factories 文件上。 我们检查这个在 spring-boot-autoconfigure.jar 中的文件的内容。 这是一个摘录:

...

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,
org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,
...

现在让我们看一下他们的内容。例如,类 JpaRepositoriesAutoConfiguration

@Configuration
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true",  matchIfMissing = true)
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class JpaRepositoriesAutoConfiguration {}

有几件有趣的事情要注意:

  1. 这是一个标准的 Spring @Configuration
  2. 该类不包含 "实际" 代码,但导入另一个配置 - JpaRepositoriesAutoConfigureRegistrar,其中包含 "实际" 代码
  3. 有几个@ConditionalOnXXX 注解被使用
  4. @AutoConfigureAfter 似乎存在顺序依赖管理

第1点和第2点是不言而喻的,第4点相当简单,所以让我们来关注3。

@Conditional 注解

如果您不是刚开始使用 Spring ,您可能会了解 @Profile 注解。Profile 是将 bean 返回方法标记为可选的方式。当 Profile 被激活时,相关的 Profile 注解的方法被调用,返回的 bean 贡献给 bean factory 。

在之前,@Profile 看起来是这个样子:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Profile {
    String[] value();
}

有趣的是,@Profile 已被重写,以使用新的 @Conditional 注解:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
    String[] value();
}

基本上,@Conditional 注解只是指向一个 Condition。反过来,Condition 是具有返回布尔值的单个方法的功能界面:如果为 true,那么 @Conditional-注解过的方法由 Spring 执行,并将其返回的对象作为 bean 添加到上下文中。

Spring Boot有很多开箱即用的 condition 可供选择:

Condition 描述
OnBeanCondition 检查 bean 是否存在于 spring factory 中
OnClassCondition 检查类是否在 classpath 中
OnExpressionCondition 执行 SPeL 表达式
OnJavaCondition 检查 Java 版本
OnJndiCondition 检查 JNDI 分支是否存在
OnPropertyCondition 检查 property 是否存在
OnResourceCondition 检查 resource 是否存在
OnWebApplicationCondition 检查 WebApplicationContext 是否存在

这些可以用来组合 boolean condition

Condition 描述
AllNestedConditions AND 操作
AnyNestedConditions OR 操作
NoneNestedCondition NOT 操作

专用的 @Conditional 注解指向这些注解。 例如,@ConditionalOnMissingBean 指向 OnBeanCondition 类。

实验时间

我们来创建一个使用 @Configuration 注解的配置类。

以下方法将在所有情况下运行:

@Bean
public String string() {
    return "string()";
}

这个不会,因为 java.lang.String 是 Java API 的一部分:

@Bean
@ConditionalOnMissingClass("java.lang.String")
public String missingClassString() {
    return "missingClassString()";
}

同样的理由,这个会:

@Bean
@ConditionalOnClass(String.class)
public String classString() {
    return "classString()";
}

上面配置的分析

掌握这个新知识后,我们来分析上面的 JpaRepositoriesAutoConfiguration 类。

且仅当满足所有条件时才启用此配置:

  • @ConditionalOnBean(DataSource.class)

    在Spring上下文中有一个 DataSource 类型的 bean

  • @ConditionalOnClass(JpaRepository.class)

    JpaRepository 类在类路径上,即项目对 Spring Data JPA 有依赖关系

  • @ConditionalOnMissingBean

    在上下文中没有类型为 JpaRepositoryFactoryBean 的 bean 或 JpaRepositoryConfigExtension

  • @ConditionalOnProperty

    标准的 application.properties 文件必须包含一个名为spring.data.jpa.repositories.enabled 的属性,值为true

此外,配置将在 HibernateJpaAutoConfiguration 之后运行(如果后者被引用)。

结论

我希望我展示 Spring Boot starter 不是魔术。下周加入我们,做一个简单的案例研究。

results matching ""

    No results matching ""