优化@Import

master
xuchengsheng 2023-10-12 22:12:08 +08:00
parent c81ce0bb66
commit 226acc6857
1 changed files with 68 additions and 39 deletions

View File

@ -2,22 +2,25 @@
- [@Import](#import)
- [一、注解描述](#一注解描述)
- [二、接口源码](#二接口源码)
- [二、注解源码](#二注解源码)
- [三、主要功能](#三主要功能)
- [四、最佳实践](#四最佳实践)
- [五、时序图](#五时序图)
- [六、源码分析](#六源码分析)
- [七、注意事项](#七注意事项)
- [八、总结](#八总结)
- [8.1、最佳实践总结](#81最佳实践总结)
- [8.2、源码分析总结](#82源码分析总结)
- [最佳实践总结](#最佳实践总结)
- [源码分析总结](#源码分析总结)
### 一、基本信息
### 一、注解描述
✒️ **作者** - Lex 📝 **博客** - [我的CSDN](https://blog.csdn.net/duzhuang2399/article/details/132806548) 📚 **文章目录** - [所有文章](https://github.com/xuchengsheng/spring-reading) 🔗 **源码地址** - [@Import源码](https://github.com/xuchengsheng/spring-reading/tree/master/spring-annotation/spring-annotation-import)
### 二、注解描述
`@Import` 是 Spring 框架的核心注解,用于导入配置类或组件到当前的 Spring 上下文中。它可以用于导入常规的 `@Configuration` 类、常规组件类,或实现了 `ImportSelector``ImportBeanDefinitionRegistrar` 接口的类。`ImportSelector` 允许根据条件动态地选择要导入的组件,而 `ImportBeanDefinitionRegistrar` 提供了一种以编程方式注册bean的方法。使用 `@Import` 注解,我们可以更灵活、模块化地组织 Spring 的配置,确保上下文中有所需的所有组件和配置。
### 二、接口源码
### 三、注解源码
`@Import` 是 Spring 框架自 3.0 版本开始引入的一个核心注解。允许我们导入一个或多个组件类,这些类通常是 `@Configuration` 类。它在功能上相当于 Spring XML 中的 `<import/>` 元素,导入类型`@Configuration`类、`ImportSelector`、`ImportBeanDefinitionRegistrar`的实现以及其他常规组件类,在导入的 `@Configuration` 类中声明的 bean 定义应使用 `@Autowired` 进行注入。
@ -63,17 +66,21 @@ public @interface Import {
}
```
### 、主要功能
### 、主要功能
**导入配置类**:允许一个 `@Configuration` 类引入另一个 `@Configuration` 类。
1. **导入配置类**
+ 允许一个 `@Configuration` 类引入另一个 `@Configuration` 类。
**导入选择器**:通过实现 `ImportSelector` 接口,可以动态地选择和导入配置类。
2. **导入选择器**
+ 通过实现 `ImportSelector` 接口,可以动态地选择和导入配置类。
**手动注册Bean**:通过实现 `ImportBeanDefinitionRegistrar` 接口,可以在运行时手动注册 bean。
3. **手动注册Bean**
+ 通过实现 `ImportBeanDefinitionRegistrar` 接口,可以在运行时手动注册 bean。
**导入常规组件类**:从 Spring 4.2 开始,还可以导入常规的组件类。
4. **导入常规组件类**
+ 从 Spring 4.2 开始,还可以导入常规的组件类。
### 、最佳实践
### 、最佳实践
首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`此类是使用Java注解来配置Spring容器的方式构造参数我们给定了一个`MyConfiguration`组件类然后遍历并打印所有的bean定义名。
@ -154,7 +161,7 @@ public class MyBeanC {
}
```
### 、时序图
### 、时序图
~~~mermaid
sequenceDiagram
@ -210,7 +217,7 @@ sequenceDiagram
MyImportBeanDefinitionRegistrar->>DefaultListableBeanFactory:registerBeanDefinition(beanName,beanDefinition)<br>在bean工厂中注册bean定义
~~~
### 、源码分析
### 、源码分析
首先来看看启动类入口,上下文环境使用`AnnotationConfigApplicationContext`此类是使用Java注解来配置Spring容器的方式构造参数我们给定了一个`MyConfiguration`组件类然后遍历并打印所有的bean定义名。
@ -718,49 +725,71 @@ public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegi
}
```
### 、注意事项
### 、注意事项
**避免循环引用**:确保您没有创建循环引用,即一个配置类导入另一个配置类,而后者又反过来导入前者。
1. **避免循环引用**
+ 确保我们没有创建循环引用,即一个配置类导入另一个配置类,而后者又反过来导入前者。
**与`@ComponentScan`的关系**`@Import`和`@ComponentScan`都可以用于注册bean但是它们的用途稍有不同。`@ComponentScan`用于自动扫描和注册bean而`@Import`用于明确地导入其他配置类。
2. **与`@ComponentScan`的关系**
+ `@Import`和`@ComponentScan`都可以用于注册bean但是它们的用途稍有不同。`@ComponentScan`用于自动扫描和注册bean而`@Import`用于明确地导入其他配置类。
**属性覆盖**如果从多个配置类中导入相同的bean定义并设置了不同的属性或值那么后导入的bean定义将覆盖先导入的bean定义。
3. **属性覆盖**
+ 如果从多个配置类中导入相同的bean定义并设置了不同的属性或值那么后导入的bean定义将覆盖先导入的bean定义。
**与`@Profile`的关系**:可以结合使用`@Import`和`@Profile`,这样只有在特定的激活配置文件中才会导入某个配置类。
4. **与`@Profile`的关系**
+ 可以结合使用`@Import`和`@Profile`,这样只有在特定的激活配置文件中才会导入某个配置类。
### 、总结
### 、总结
#### 8.1、最佳实践总结
#### 最佳实践总结
**启动类**:通过`ImportApplication`类我们初始化了Spring的上下文环境选择了`AnnotationConfigApplicationContext`这是Spring提供的使用Java注解配置方式。在该上下文中我们指定了`MyConfiguration`作为主要的配置类并遍历了上下文中所有已注册的bean名称。
1. **启动类**
+ 通过`ImportApplication`类我们初始化了Spring的上下文环境选择了`AnnotationConfigApplicationContext`这是Spring提供的使用Java注解配置方式。在该上下文中我们指定了`MyConfiguration`作为主要的配置类并遍历了上下文中所有已注册的bean名称。
**主配置类**`MyConfiguration`类通过使用`@Import`注解导入了四种不同类型的组件或选择器:
2. **主配置类**
- `MyBean`一个普通的Java类。
- `MyImportSelector`:一个实现了`ImportSelector`接口的类。
- `MyDeferredImportSelector`:一个实现了`DeferredImportSelector`接口的类。
- `MyImportBeanDefinitionRegistrar`:一个实现了`ImportBeanDefinitionRegistrar`接口的类。
+ `MyConfiguration`类通过使用`@Import`注解导入了四种不同类型的组件或选择器:
**选择器与注册器**
- `MyBean`一个普通的Java类。
- `MyImportSelector`通过实现`ImportSelector`接口,动态地选择并导入了`MyBeanA`类。
- `MyDeferredImportSelector`作为`DeferredImportSelector`的实现,延迟地选择并导入了`MyBeanB`类。与`ImportSelector`不同,它允许在所有其他配置类被处理后再进行导入。
- `MyImportBeanDefinitionRegistrar`提供了手动注册bean的功能。在这个示例中它将`MyBeanC`手动注册到了Spring上下文中。
- `MyImportSelector`:一个实现了`ImportSelector`接口的类。
**组件定义**示例中包含四个Java类分别为`MyBean`、`MyBeanA`、`MyBeanB`和`MyBeanC`它们都被上述的选择器或注册器选择并导入到Spring上下文中
- `MyDeferredImportSelector`:一个实现了`DeferredImportSelector`接口的类。
#### 8.2、源码分析总结
- `MyImportBeanDefinitionRegistrar`:一个实现了`ImportBeanDefinitionRegistrar`接口的类。
**启动阶段**在Spring启动时会使用`AnnotationConfigApplicationContext`来加载和解析配置类,这个配置类可能包含有`@Import`注解。
**解析配置类**:在`AnnotationConfigApplicationContext`的构造函数中,通过`refresh()`方法开始刷新容器并解析Bean定义。进一步在`AbstractApplicationContext#refresh`方法中,会调用`invokeBeanFactoryPostProcessors`方法来处理在上下文中注册为bean的工厂处理器。
3. **选择器与注册器**
**处理`@Import`注解**:处理过程中会特别关注`BeanDefinitionRegistryPostProcessor`,这是`BeanFactoryPostProcessor`的子接口专门用于在所有其他bean定义加载之前修改默认的bean定义。然后系统解析配置类并验证其内容这主要涉及查找该类中的@Bean方法、组件扫描指令等并将这些信息注册到Spring容器中
- `MyImportSelector`通过实现`ImportSelector`接口,动态地选择并导入了`MyBeanA`类
**处理`ImportSelector`和`DeferredImportSelector`**:在上述解析过程中,如果配置类包含一个实现了`ImportSelector`或`DeferredImportSelector`接口的类,那么它们将被用来选择并导入其他的类。在示例中,`MyImportSelector`和`MyDeferredImportSelector`是这样的选择器,分别导入`MyBeanA`和`MyBeanB`
- `MyDeferredImportSelector`作为`DeferredImportSelector`的实现,延迟地选择并导入了`MyBeanB`类。与`ImportSelector`不同,它允许在所有其他配置类被处理后再进行导入
**处理`ImportBeanDefinitionRegistrar`**在加载Bean定义的过程中也会处理与`ConfigurationClass`关联的所有`ImportBeanDefinitionRegistrar`。这些注册器允许在解析时动态地、以编程方式地添加额外的Bean定义如`MyImportBeanDefinitionRegistrar`示例中为`MyBeanC`进行的手动注册
- `MyImportBeanDefinitionRegistrar`提供了手动注册bean的功能。在这个示例中它将`MyBeanC`手动注册到了Spring上下文中
**注册导入的配置类**:如果一个`@Configuration`类是由于`@Import`或其他机制导入的Spring不仅会处理它的`@Bean`方法和其他注解还会为这个类本身注册一个Bean定义以便它也可以被注入到其他Bean或由应用程序检索。
**额外Bean的注册**:通过`ImportBeanDefinitionRegistrar`和其自定义逻辑能够根据需要将任意数量的额外Bean定义注册到容器中。
4. **组件定义**
+ 在我们最佳实践中包含四个Java类分别为`MyBean`、`MyBeanA`、`MyBeanB`和`MyBeanC`它们都被上述的选择器或注册器选择并导入到Spring上下文中。
#### 源码分析总结
1. **启动阶段**
+ 在Spring启动时会使用`AnnotationConfigApplicationContext`来加载和解析配置类,这个配置类可能包含有`@Import`注解。
2. **解析配置类**
+ 在`AnnotationConfigApplicationContext`的构造函数中,通过`refresh()`方法开始刷新容器并解析Bean定义。进一步在`AbstractApplicationContext#refresh`方法中,会调用`invokeBeanFactoryPostProcessors`方法来处理在上下文中注册为bean的工厂处理器。
3. **处理`@Import`注解**
+ 处理过程中会特别关注`BeanDefinitionRegistryPostProcessor`,这是`BeanFactoryPostProcessor`的子接口专门用于在所有其他bean定义加载之前修改默认的bean定义。然后系统解析配置类并验证其内容这主要涉及查找该类中的@Bean方法、组件扫描指令等并将这些信息注册到Spring容器中。
4. **处理`ImportSelector`和`DeferredImportSelector`**
+ 在上述解析过程中,如果配置类包含一个实现了`ImportSelector`或`DeferredImportSelector`接口的类,那么它们将被用来选择并导入其他的类。在示例中,`MyImportSelector`和`MyDeferredImportSelector`是这样的选择器,分别导入`MyBeanA`和`MyBeanB`。
5. **处理`ImportBeanDefinitionRegistrar`**
+ 在加载Bean定义的过程中也会处理与`ConfigurationClass`关联的所有`ImportBeanDefinitionRegistrar`。这些注册器允许在解析时动态地、以编程方式地添加额外的Bean定义如`MyImportBeanDefinitionRegistrar`示例中为`MyBeanC`进行的手动注册。
6. **注册导入的配置类**
+ 如果一个`@Configuration`类是由于`@Import`或其他机制导入的Spring不仅会处理它的`@Bean`方法和其他注解还会为这个类本身注册一个Bean定义以便它也可以被注入到其他Bean或由应用程序检索。
7. **额外Bean的注册**
+ 通过`ImportBeanDefinitionRegistrar`和其自定义逻辑能够根据需要将任意数量的额外Bean定义注册到容器中。