SpringBoot - 自动装配

欢乐豆123 / 2024-08-08 / 原文

SpringBoot - 自动装配

SpringBoot 最核心的功能就是自动装配,

Starter 作为 SpringBoot 的核心功能之一,基于自动配置代码提供了自动配置模块及依赖的能力,让软件集成变得简单、易用。使用 SpringBoot 时,我们只需引人对应的 Starter,SpringBoot 启动时便会自动加载相关依赖,集成相关功能,这便是 SpringBoot 的自动装配功能。

简单概括其自动配置的原理:由@SpringBootAppliction组合注解中的@EnableAutoConfiguration注解开启自动配置,加载 spring.factories 文件中注册的各种 AutoConfiguration 配置类,当其 @Conditional 条件注解生效时,实例化该配置类中定义的 Bean,并注入 Spring 上下文。

 


SpringBoot 自动装配过程涉及以下主要内容:@EnableAutoConfiguration: 扫描类路径下的META-INF/spring.factories文件,并加载其中中注册的 AutoConfiguration 配置类,开启自动装配;

spring.factories:配置文件,位于 jar 包的 META-INF 目录下,按照指定格式注册了 AutoConfiguration 配置类;AutoConfiguration 类:自动配置类,SpringBoot 的大量以 xxxAutoConfiguration 命名的自动配置类,定义了三方组件集成 Spring 所需初始化的 Bean 和条件;@Conditional 条件注解及其行生注解:使用在 AutoConfiguration 类上,设置了配置类的实例化条件;Starter:三方组件的依赖及配置,包括 SpringBoot 预置组件和自定义组件,往往会包含 spring.factories 文件、AutoConfiguration 类和其他配置类。其功能间的作用关系如下图:


1. @EnableAutoConfiguration

2. @AutoConfigurationImportSelector

@EnableAutoConfiguration的自动配置功能是通过@Import注解导入的ImportSelector来完成的。 @Import(AutoConfigurationlmportSelector.class)是@EnableAutoConfiguration注解的组成部分,也是自动配置功能的核心实现者。下面讲解@Import的基本使用方法和ImportSelector的实现类AutoConfigurationlmportSelector。
2. @Import
@Import注解,提供了导入配置类的功能。SpringBoot 的源代码中,有大量的EnableXXX类都使用了该注解,了解@Import有助于我们理解 SpringBoot 的自动装配,@Import有以下三个用途:

通过@Import引入@Configuration注解的类;
导入实现了ImportSelector或ImportBeanDefinitionRegistrar的类;
通过@lmport导入普通的POJO。

3. AutoConfigurationlmportSelector 实现类
@Import的许多功能都需要借助接口ImportSelector来实现,ImportSelector决定可引人哪些 @Configuration的注解类,ImportSelector接口源码如下:
java 代码解读复制代码public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}

ImportSelector接口只提供了一个参数为AnnotationMetadata的方法,返回的结果为一个字符串数组。其中参数AnnotationMetadata内包含了被@Import注解的类的注解信息。在selectimports方法内可根据具体实现决定返回哪些配置类的全限定名,将结果以字符串数组的形式返回。如果实现了接口ImportSelector的类的同时又实现了以下4个Aware接口,那么 Spring 保证在调用ImportSelector之前会先调用Aware接口的方法。这4个接口为:EnvironmentAware、BeanFactoryAware、 BeanClassLoaderAware和ResourceLoaderAware。
在AutoConfigurationlmportSelector的源代码中就实现了这4个接口:


  四、@Conditional 注解

  @Conditional 注解允许你根据特定的条件来动态配置 Spring Bean。换句话说,只有在特定条件满足的情况下,Spring 才会实例化和注入标注了 @Conditional 的 Bean。

  @Conditional 注解本身并不包含具体的条件逻辑,而是依赖于实现了 Condition 接口的类来定义条件逻辑。使用时需要将实现了 Condition 接口的类作为参数传递给 @Conditional 注解。

   1.  Condition 接口

1 package org.springframework.context.annotation;
2 
3 import org.springframework.core.type.AnnotatedTypeMetadata;
4 
5 @FunctionalInterface
6 public interface Condition {
7     // 决定条件是否匹配
8     boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
9 }

   matches方法的第一个参数为ConditionContext,可通过该接口提供的方法来获得 Spring 应用的上下文信息;matches方法的第二个参数为AnnotatedTypeMetadata,该接口提供了访问特定类或方法的注解功能,可以用来检查带有@Bean注解的方法上是否还有其他注解。

   2. conditional

1 @Target({ElementType.TYPE, ElementType.METHOD})
2 @Retention(RetentionPolicy.RUNTIME)
3 @Documented
4 public @interface Conditional {
5     Class<? extends Condition>[] value();
6 }

  @Conditional 注解唯一的元素属性value是接口 Condition 的数组,只有在数组中指定的所有Conditionmatches方法都返回true的情况下,被注解的类才会被加载

  3. @Conditional 的衍生注解

  Spring 提供了一些常用的 @Conditional 注解,用于处理常见的条件逻辑:

  •   @ConditionalOnProperty: 根据配置属性来判断是否注入 Bean。
  •   @ConditionalOnClass: 当类路径中存在特定的类时注入 Bean。
  •   @ConditionalOnMissingBean: 当容器中不存在特定的 Bean 时注入。
  •   @ConditionalOnBean: 当容器中存在特定的 Bean 时注入。
  •   @ConditionalOnWebApplication: 只有在 Web 应用环境中才注入 Bean。
  •   @ConditionalOnExpression: 根据 SpEL 表达式的结果来判断是否注入 Bean。

   通过一个举例,我们看下衍生注解是如何发挥作用的。

   1)spring.factories文件

   我们在spring.factories文件中看到这一行自动配置类:

   

   2)SpringApplicationAdminJmxAutoConfiguration实现类

   其中有一个SpringApplicationAdminJmxAutoConfiguration实现类

1 # Auto Configure
2 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
3 org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
4 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
5 org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
6 org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
7 org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

    找到SpringApplicationAdminJmxAutoConfiguration这个类的源码:

 1 @AutoConfiguration(
 2     after = {JmxAutoConfiguration.class}
 3 )
 4 @ConditionalOnProperty(
 5     prefix = "spring.application.admin",
 6     value = {"enabled"},
 7     havingValue = "true",
 8     matchIfMissing = false
 9 )
10 public class SpringApplicationAdminJmxAutoConfiguration {
11     
12     //...
13     
14     public SpringApplicationAdminJmxAutoConfiguration() {
15     }
16 
17     //...
18 }    

   3)@ConditionalOnProperty 注解

 1 @Retention(RetentionPolicy.RUNTIME)
 2 @Target({ElementType.TYPE, ElementType.METHOD})
 3 @Documented
 4 @Conditional({OnPropertyCondition.class})
 5 public @interface ConditionalOnProperty {
 6     String[] value() default {};
 7 
 8     String prefix() default "";
 9 
10     String[] name() default {};
11 
12     String havingValue() default "";
13 
14     boolean matchIfMissing() default false;
15 }

  这个注解中,各个属性的作用
  1.  prefix  指定了属性的前缀

  比如:prefix = "spring.application.admin",它表示所有相关属性都应该以 spring.application.admin 开头。

  例如,这里的完整属性名是 spring.application.admin.enabled。

  2. value  指定了具体的属性名

  value = {"enabled"},在这个例子中,它是 enabled。

  与前缀结合,这个属性的完整名是 spring.application.admin.enabled。

  3. havingValue  指定了配置属性的期望值

  havingValue = "true",只有当 spring.application.admin.enabled 的值为 true 时,条件才会满足。

  例如,只有在 application.properties 或 application.yml 文件中配置了 spring.application.admin.enabled=true 时,注解标注的配置才会生效。

  4.  matchIfMissing  指定了当配置属性缺失时是否默认匹配

  matchIfMissing = false

  如果 matchIfMissing 设置为 true,即使配置文件中没有 spring.application.admin.enabled 属性,条件也会匹配。

  如果 matchIfMissing 设置为 false(如本例), 当配置文件中没有 spring.application.admin.enabled 属性时,条件不会匹配。


  参考链接:https://juejin.cn/post/7137095650160115743