spring-tutorial/docs/01.Java/13.框架/01.Spring/01.Spring核心/01.SpringBean.md

214 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Spring Bean
date: 2021-12-10 19:15:42
order: 01
categories:
- Java
- 框架
- Spring
- Spring核心
tags:
- Java
- 框架
- Spring
- Bean
- BeanDefinition
permalink: /pages/68097d/
---
# Spring Bean
在 Spring 中,构成应用程序主体由 Spring IoC 容器管理的对象称为 Bean。**Bean 是由 Spring IoC 容器实例化、装配和管理的对象**。 Bean 以及它们之间的依赖关系反映在容器使用的配置元数据中。
## Spring Bean 定义
### BeanDefinition
Spring IoC 容器本身,并不能识别配置的元数据。为此,要将这些配置信息转为 Spring 能识别的格式——`BeanDefinition` 对象。
**`BeanDefinition` 是 Spring 中定义 Bean 的配置元信息接口**,它包含:
- Bean 类名
- Bean 行为配置元素,如:作用域、自动绑定的模式、生命周期回调等
- 其他 Bean 引用也可称为合作者Collaborators或依赖Dependencies
- 配置设置,如 Bean 属性Properties
#### BeanDefinition 元信息
`BeanDefinition` 元信息如下:
| 属性Property | 说明 |
| ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| [Class](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-class) | 全类名,必须是具体类,不能用抽象类或接口 |
| [Name](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-beanname) | Bean 的名称或者 ID |
| [Scope](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes) | Bean 的作用域(如:`singleton`、`prototype` 等) |
| [Constructor arguments](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-collaborators) | Bean 构造器参数(用于依赖注入) |
| [Properties](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-collaborators) | Bean 属性设置(用于依赖注入) |
| [Autowiring mode](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-autowire) | Bean 自动绑定模式(如:通过名称 byName |
| [Lazy initialization mode](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-lazy-init) | Bean 延迟初始化模式(延迟和非延迟) |
| [Initialization method](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-lifecycle-initializingbean) | Bean 初始化回调方法名称 |
| [Destruction method](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-lifecycle-disposablebean) | Bean 销毁回调方法名称 |
#### BeanDefinition 构建
BeanDefinition 构建方式:
- 通过 `BeanDefinitionBuilder`
- 通过 `AbstractBeanDefinition` 以及派生类
> 💻 Spring Bean 定义示例源码:[BeanDefinitionTests](https://github.com/dunwu/spring-tutorial/blob/master/codes/core/spring-core-ioc/src/test/java/io/github/dunwu/spring/core/bean/BeanDefinitionTests.java)
### Spring Bean 命名
#### Spring Bean 命名规则
每个 Bean 拥有一个或多个标识符identifiers这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符如果需要额外的可考虑使用别名Alias来扩充。
在基于 XML 的配置元信息中,开发人员**可以使用 `id` 属性、`name` 属性或来指定 Bean 标识符**。通常Bean 的标识符由字母组成,允许出现特殊字符。如果要想引入 Bean 的别名的话,可在 `name` 属性使用半角逗号(“,”)或分号(“;”) 来间隔。
Spring 中,**为 Bean 指定 `id``name` 属性不是必须的**。如果不指定Spring 会自动为 Bean 分配一个唯一的名称。尽管 Bean 的命名没有限制,不过**官方建议采用驼峰命名法来命名 Bean**。
#### Spring Bean 命名生成器
Spring 提供了两种 Spring Bean 命名生成器:
- `DefaultBeanNameGenerator`:默认通用 `BeanNameGenerator` 实现。
- `AnnotationBeanNameGenerator`:基于注解扫描的 `BeanNameGenerator` 实现。
```java
public interface BeanNameGenerator {
String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}
```
#### Spring Bean 别名
Spring 支持通过 `<alias>` 属性为 Bean 设置别名。
Bean 别名Alias的作用
- 复用现有的 `BeanDefinition`
- 更具有场景化的命名方法,比如:
- `<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>`
- `<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>`
```xml
<bean id="user" class="io.github.dunwu.spring.core.bean.entity.person.User">
<!-- 属性略 -->
</bean>
<alias name="user" alias="aliasUser" />
```
## Spring Bean 生命周期
![](https://raw.githubusercontent.com/dunwu/images/master/snap/20211201102734.png)
1. Spring 对 Bean 进行实例化(相当于 new XXX()
2. Spring 将值和引用注入到 Bean 对应的属性中
3. 如果 Bean 实现了 `BeanNameAware` 接口Spring 将 Bean 的 ID 传递给 `setBeanName` 方法
- 作用是通过 Bean 的引用来获得 Bean ID一般业务中是很少有用到 Bean 的 ID 的
4. 如果 Bean 实现了 `BeanFactoryAware` 接口Spring 将调用 `setBeanDactory` 方法,并把 `BeanFactory` 容器实例作为参数传入。
- 作用是获取 Spring 容器,如 Bean 通过 Spring 容器发布事件等
5. 如果 Bean 实现了 `ApplicationContextAware` 接口Spring 容器将调用 `setApplicationContext` 方法,把应用上下文作为参数传入
- 作用与 `BeanFactory` 类似都是为了获取 Spring 容器,不同的是 Spring 容器在调用 `setApplicationContext` 方法时会把它自己作为 `setApplicationContext` 的参数传入,而 Spring 容器在调用 `setBeanFactory` 前需要使用者自己指定(注入)`setBeanFactory` 里的参数 `BeanFactory`
6. 如果 Bean 实现了 `BeanPostProcess` 接口Spring 将调用 `postProcessBeforeInitialization` 方法
- 作用是在 Bean 实例创建成功后对其进行增强处理,如对 Bean 进行修改,增加某个功能
7. 如果 Bean 实现了 `InitializingBean` 接口Spring 将调用 `afterPropertiesSet` 方法,作用与在配置文件中对 Bean 使用 `init-method` 声明初始化的作用一样,都是在 Bean 的全部属性设置成功后执行的初始化方法。
8. 如果 Bean 实现了 `BeanPostProcess` 接口Spring 将调用 `postProcessAfterInitialization` 方法
- `postProcessBeforeInitialization` 是在 Bean 初始化前执行的,而 `postProcessAfterInitialization` 是在 Bean 初始化后执行的
9. 经过以上的工作后Bean 将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁
10. 如果 Bean 实现了 `DispostbleBean` 接口Spring 将调用它的 `destory` 方法,作用与在配置文件中对 Bean 使用 `destory-method` 属性的作用一样,都是在 Bean 实例销毁前执行的方法。
## Spring Bean 注册
注册 Spring Bean 实际上是将 `BeanDefinition` 注册到 IoC 容器中。
### XML 配置元信息
Spring 的传统配置方式。在 `<bean>` 标签中配置元数据内容。
缺点是当 JavaBean 过多时,产生的配置文件足以让你眼花缭乱。
### 注解配置元信息
使用 `@Bean`、`@Component`、`@Import` 注解注册 Spring Bean。
### Java API 配置元信息
- 命名方式:`BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)`
- 非命名方式:`BeanDefinitionReaderUtils#registerWithGeneratedName(AbstractBeanDefinition,BeanDefinitionRegistry)`
- 配置类方式:`AnnotatedBeanDefinitionReader#register(Class...)`
> 💻 Spring Bean 注册示例源码:[BeanRegistryTests](https://github.com/dunwu/spring-tutorial/blob/master/codes/core/spring-core-ioc/src/test/java/io/github/dunwu/spring/core/bean/BeanRegistryTests.java)
## Spring Bean 实例化
Spring Bean 实例化方式:
- 常规方式
- 通过构造器配置元信息XML、Java 注解和 Java API
- 通过静态方法配置元信息XML、Java 注解和 Java API
- 通过 Bean 工厂方法配置元信息XML、Java 注解和 Java API
- 通过 `FactoryBean`配置元信息XML、Java 注解和 Java API
- 特殊方式
- 通过 `ServiceLoaderFactoryBean`配置元信息XML、Java 注解和 Java API
- 通过 `AutowireCapableBeanFactory#createBean(java.lang.Class, int, boolean)`
- 通过 `BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)`
> 💻 Spring Bean 实例化示例源码:[BeanInstantiationTests](https://github.com/dunwu/spring-tutorial/blob/master/codes/core/spring-core-ioc/src/test/java/io/github/dunwu/spring/core/bean/BeanInstantiationTests.java)、[BeanInstantiationSpecialTests](https://github.com/dunwu/spring-tutorial/blob/master/codes/core/spring-core-ioc/src/test/java/io/github/dunwu/spring/core/bean/BeanInstantiationSpecialTests.java)
## Spring Bean 初始化和销毁
Spring Bean 初始化和销毁的方式有以下几种:
1. 使用 `@PostConstruct``@PreDestroy` 注解分别指定相应的初始化方法和销毁方法。
2. 实现 `InitializingBean` 接口的 `afterPropertiesSet()` 方法来编写初始化方法;实现 `DisposableBean` 接口的 `destroy()` 方法来编写销毁方法。
- `InitializingBean` 接口包含一个 `afterPropertiesSet` 方法,可以通过实现该接口,然后在这个方法中编写初始化逻辑。
- `DisposableBean`接口包含一个 `destory` 方法,可以通过实现该接口,然后在这个方法中编写销毁逻辑。
3. 自定义初始化方法
- XML 配置:`<bean init-method="init" destroy="destroy" ... />`
- Java 注解:`@Bean(initMethod = "init", destroyMethod = "destroy")`
- Java API`AbstractBeanDefinition#setInitMethodName(String)` 和 `AbstractBeanDefinition#setDestroyMethodName(String)` 分别定义初始化和销毁方法
注意:如果同时存在,执行顺序会按照序列执行。
Bean 的延迟初始化
- xml 方式:`<bean lazy-init="true" ... />`
- 注解方式:`@Lazy`
Spring 提供了一个 `BeanPostProcessor` 接口,提供了两个方法 `postProcessBeforeInitialization``postProcessAfterInitialization`。其中`postProcessBeforeInitialization` 在组件的初始化方法调用之前执行,`postProcessAfterInitialization` 在组件的初始化方法调用之后执行。它们都包含两个入参:
- `bean`:当前组件对象;
- `beanName`:当前组件在容器中的名称。
> 💻 Spring Bean 初始化和销毁示例源码:[BeanInitDestroyTests](https://github.com/dunwu/spring-tutorial/blob/master/codes/core/spring-core-ioc/src/test/java/io/github/dunwu/spring/core/bean/BeanInitDestroyTests.java)
## Spring Bean 垃圾回收
Spring Bean 垃圾回收步骤:
1. 关闭 Spring 容器(应用上下文)
2. 执行 GC
3. Spring Bean 覆盖的 `finalize()` 方法被回调
## Spring Bean 作用范围
| Scope | Description |
| :---------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [singleton](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-singleton) | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. |
| [prototype](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-prototype) | Scopes a single bean definition to any number of object instances. |
| [request](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-request) | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring `ApplicationContext`. |
| [session](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-session) | Scopes a single bean definition to the lifecycle of an HTTP `Session`. Only valid in the context of a web-aware Spring `ApplicationContext`. |
| [application](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-application) | Scopes a single bean definition to the lifecycle of a `ServletContext`. Only valid in the context of a web-aware Spring `ApplicationContext`. |
| [websocket](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#websocket-stomp-websocket-scope) | Scopes a single bean definition to the lifecycle of a `WebSocket`. Only valid in the context of a web-aware Spring `ApplicationContext`. |
## 参考资料
- [Spring 官方文档之 Core Technologies](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans)
- [《小马哥讲 Spring 核心编程思想》](https://time.geekbang.org/course/intro/265)