Spring Bean 简介
如何注册一个 Spring Bean?
通过 BeanDefinition 和外部单体对象来注册
BeanDefinition
什么是 BeanDefinition
BeanDefinition
是 Spring Framework 中定义 Bean 的配置元信息接口,包含:
- Bean 类名
- Bean 行为配置元素,如:作用域、自动绑定的模式、生命周期回调等
- 其他 Bean 引用
- 配置设置,如 Bean 属性(Properties)
l#beandefinition-元信息)BeanDefinition 元信息
BeanDefinition
元信息如下:
属性(Property) | 说明 |
---|---|
ClassBean | 全类名,必须是具体类,不能用抽象类或接口 |
NameBean | 的名称或者ID |
ScopeBean | 的作用域(如:singleton、prototype等) |
ConstructorargumentsBean | 构造器参数(用于依赖注入) |
PropertiesBean | 属性设置(用于依赖注入) |
AutowiringmodeBean | 自动绑定模式(如:通过名称byName) |
LazyinitializationmodeBean | 延迟初始化模式(延迟和非延迟) |
InitializationmethodBean | 初始化回调方法名称 |
DestructionmethodBean | 销毁回调方法名称 |
BeanDefinition 构建
BeanDefinition 构建方式:
- 通过
BeanDefinitionBuilder
- 通过
AbstractBeanDefinition
以及派生类
// 1.通过 BeanDefinitionBuilder 构建
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
// 通过属性设置
beanDefinitionBuilder
.addPropertyValue("id", 1)
.addPropertyValue("name", "小马哥");
// 获取 BeanDefinition 实例
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// BeanDefinition 并非 Bean 终态,可以自定义修改
// 2. 通过 AbstractBeanDefinition 以及派生类
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
// 设置 Bean 类型
genericBeanDefinition.setBeanClass(User.class);
// 通过 MutablePropertyValues 批量操作属性
MutablePropertyValues propertyValues = new MutablePropertyValues();
// propertyValues.addPropertyValue("id", 1);
// propertyValues.addPropertyValue("name", "小马哥");
propertyValues
.add("id", 1)
.add("name", "小马哥");
// 通过 set MutablePropertyValues 批量操作属性
genericBeanDefinition.setPropertyValues(propertyValues);
命名 Spring Bean
Spring Bean 命名规则
每个 Bean 拥有一个或多个标识符(identifiers),这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名(Alias)来扩充。
在基于 XML 的配置元信息中,开发人员可用 id 或者 name 属性来规定 Bean 的标识符。通常 Bean 的标识符由字母组成,允许出现特殊字符。如果要想引入 Bean 的别名的话,可在 name 属性使用半角逗号(“,”)或分号(“;”) 来间隔。
Bean 的 id 或 name 属性并非必须制定,如果留空的话,容器会为 Bean 自动生成一个唯一的名称。Bean 的命名尽管没有限制,不过官方建议采用驼峰的方式,更符合 Java 的命名约定。
Spring Bean 命名生成器
Spring 提供了两种 Spring Bean 命名生成器:
- DefaultBeanNameGenerator:默认通用 BeanNameGenerator 实现。
- AnnotationBeanNameGenerator:基于注解扫描的 BeanNameGenerator 实现。
public interface BeanNameGenerator {
String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
}
Spring Bean 别名
可能存在这样的场景,不同系统中对于同一 bean 的命名方式不一样。 为了适配,Spring 支持 <alias>
为 bean 添加别名的功能。
<bean id="user" class="io.github.dunwu.spring.core.domain.User"
p:name="张三" p:age="18">
</bean>
<alias name="sysUser" alias="superUser" />
Bean 别名(Alias)的作用:
-
复用现有的 BeanDefinition
-
更具有场景化的命名方法,比如:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
Bean 生命周期流程
-
Spring 对 Bean 进行实例化(相当于 new XXX())
-
Spring 将值和引用注入进 Bean 对应的属性中
-
如果 Bean 实现了
BeanNameAware
接口,Spring 将 Bean 的 ID 传递给setBeanName
方法- 作用是通过 Bean 的引用来获得 Bean ID,一般业务中是很少有用到 Bean 的 ID 的
-
如果 Bean 实现了
BeanFactoryAware
接口,Spring 将调用setBeanDactory
方法,并把BeanFactory
容器实例作为参数传入。- 作用是获取 Spring 容器,如 Bean 通过 Spring 容器发布事件等
-
如果 Bean 实现了
ApplicationContextAware
接口,Spring 容器将调用setApplicationContext
方法,把应用上下文作为参数传入- 作用与
BeanFactory
类似都是为了获取 Spring 容器,不同的是 Spring 容器在调用setApplicationContext
方法时会把它自己作为setApplicationContext
的参数传入,而 Spring 容器在调用setBeanFactory
前需要使用者自己指定(注入)setBeanFactory
里的参数BeanFactory
- 作用与
-
如果 Bean 实现了
BeanPostProcess
接口,Spring 将调用postProcessBeforeInitialization
方法- 作用是在 Bean 实例创建成功后对其进行增强处理,如对 Bean 进行修改,增加某个功能
-
如果 Bean 实现了
InitializingBean
接口,Spring 将调用afterPropertiesSet
方法,作用与在配置文件中对 Bean 使用init-method
声明初始化的作用一样,都是在 Bean 的全部属性设置成功后执行的初始化方法。 -
如果 Bean 实现了
BeanPostProcess
接口,Spring 将调用postProcessAfterInitialization
方法postProcessBeforeInitialization
是在 Bean 初始化前执行的,而postProcessAfterInitialization
是在 Bean 初始化后执行的
-
经过以上的工作后,Bean 将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁
-
如果 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,Be
anDefinitionRegistry)
- 配置类方式:
AnnotatedBeanDefinitionReader#register(Class...)
[示例]
@Import(AnnotationComponentScan.MyConfiguration.class)
public class AnnotationComponentScan {
public static void main(String[] args) {
// 创建 BeanFactory 容器
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
// 1.注册配置类
ctx.register(AnnotationComponentScan.class);
// 2.通过 Java API 注册
registerBeanDefinition(ctx, "zhaoliu", User.class);
// 启动应用上下文
ctx.refresh();
User wangwu = (User) ctx.getBean("wangwu");
System.out.println("wangwu info: " + wangwu);
System.out.println("All beans of User: " + ctx.getBeansOfType(User.class));
//显示关闭 ApplicationContext
ctx.close();
}
public static void registerBeanDefinition(BeanDefinitionRegistry registry, String beanName, Class<?> beanClass) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClass);
builder.addPropertyValue("name", "赵六");
builder.addPropertyValue("age", 31);
// 注册 BeanDefinition
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
@Configuration
public static class MyConfiguration {
// 注解方式配置元信息
@Bean(name = { "user", "wangwu" })
public User user() {
return new User("王五", 21);
}
}
}
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 初始化和销毁
Spring Bean 初始化和销毁的方式有以下几种:
-
使用
@PostConstruct
和@PreDestroy
注解分别指定相应的初始化方法和销毁方法。 -
实现
InitializingBean
接口的afterPropertiesSet()
方法来编写初始化方法;实现DisposableBean
接口的destroy()
方法来编写销毁方法。 -
自定义初始化方法
- XML 配置:
<bean init-method="init" destroy="destroy" ... />
- Java 注解:
@Bean(initMethod = "init", destroyMethod = "destroy")
- Java API:
AbstractBeanDefinition#setInitMethodName(String)
和AbstractBeanDefinition#setDestroyMethodName(String)
分别定义初始化和销毁方法
- XML 配置:
注意:如果同时存在,执行顺序会按照序列执行。
@Bean
的 initMethod 和 destroyMethod
(1)定义 Bean 实例
public class Pojo {
public Pojo() {
System.out.println("[Pojo 构造方法]");
}
public void init() {
System.out.println("[Pojo 初始化方法]");
}
public void destroy() {
System.out.println("[Pojo 销毁方法]");
}
}
(2)使用 @Bean
在配置类中注册 Bean
@Configuration
public class AnnotationBeanDemo {
@Bean(initMethod = "init", destroyMethod = "destroy")
public Pojo pojo() {
return new Pojo();
}
}
说明:
@Bean
注解的initMethod
和destroyMethod
属性分别用于指定 Bean 对应的初始化方法和销毁方法。
(3)测试:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationBeanDemo.class);
System.out.println("AnnotationBeanDemo 示例结束");
context.close();
}
输出
[Pojo 构造方法]
[Pojo 初始化方法]
AnnotationBeanDemo 示例结束
[Pojo 销毁方法]
InitializingBean
和 DisposableBean
InitializingBean
接口包含一个afterPropertiesSet
方法,可以通过实现该接口,然后在这个方法中编写初始化逻辑。DisposableBean
接口包含一个destory
方法,可以通过实现该接口,然后在这个方法中编写销毁逻辑。
(1)定义 Bean 实例
public static class Pojo2 implements InitializingBean, DisposableBean {
public Pojo2() {
System.out.println("[Pojo2 构造方法]");
}
@Override
public void afterPropertiesSet() {
System.out.println("[Pojo2 初始化方法]");
}
@Override
public void destroy() {
System.out.println("[Pojo2 销毁方法]");
}
}
(2)使用 @Bean
在配置类中注册 Bean
@Configuration
public class AnnotationBeanDemo {
@Bean
public Pojo2 pojo2() {
return new Pojo2();
}
}
(3)测试:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AnnotationBeanDemo.class);
System.out.println("AnnotationBeanDemo 示例结束");
context.close();
}
输出
[Pojo2 构造方法]
[Pojo2 初始化方法]
AnnotationBeanDemo 示例结束
[Pojo2 销毁方法]
@PostConstruct
和 @PreDestroy
可以使用 @PostConstruct
和 @PreDestroy
注解修饰方法来指定相应的初始化和销毁方法。
BeanPostProcessor
Spring 提供了一个 BeanPostProcessor
接口,提供了两个方法 postProcessBeforeInitialization
和 postProcessAfterInitialization
。其中postProcessBeforeInitialization
在组件的初始化方法调用之前执行,postProcessAfterInitialization
在组件的初始化方法调用之后执行。它们都包含两个入参:
- bean:当前组件对象;
- beanName:当前组件在容器中的名称。
(1)定义 Pojo,如上面的例子
(2)定义 BeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor {
public MyBeanPostProcessor() {
System.out.println("[BeanPostProcessor] construct");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[BeanPostProcessor] postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[BeanPostProcessor] postProcessAfterInitialization");
return bean;
}
}
(3)使用 @Bean
在配置类中注册 Bean
@Bean
public Pojo pojo() {
return new Pojo();
}
@Bean
public MyBeanPostProcessor myBeanPostProcessor() {
return new MyBeanPostProcessor();
}
(3)测试:
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MyBeanPostProcessorDemo.class);
System.out.println("BeanPostProcessorDemo 示例结束");
context.close();
}
输出
[BeanPostProcessor] construct
[Pojo 构造方法]
[BeanPostProcessor] postProcessBeforeInitialization
[Pojo 初始化方法]
[BeanPostProcessor] postProcessAfterInitialization
BeanPostProcessorDemo 示例结束
[Pojo 销毁方法]
Spring Bean 垃圾回收
Spring Bean 垃圾回收步骤:
- 关闭 Spring 容器(应用上下文)
- 执行 GC
- Spring Bean 覆盖的 finalize() 方法被回调