在接下来的一段时间里,主要分享一些Spring相关的知识文章!
Spring官方文档
BeanDefinition元信息
BeanDefinition是定义 Bean 的配置元信息接口,主要作用是描述一个Bean,里面存储Bean的相关信息;包括类名、构造器、属性、方法、作用域、自动绑定的模式,生命周期回调等等。
概述
他的类图继承关系如下:
image-20230225171608444
从图中可以看出,BeanDefinition
从下派生出 AnnotatedBeanDefinition
接口,以及常用子类 RootBeanDefinition
、GenericBeanDefinition
等。
BeanDefinition
可以构建的信息在我们注册对象到Spring容器中(不管是使用xml、@Bean)都可以从中感受到。在Spring构建Bean的过程中,通常我们会在BeanDefinition
中进行管理的Bean元信息主要包括:
- • Bean 全类名,必须是具体类,不能用抽象类或接口
- • Bean 的名称或者 ID
- • Bean 的作用域(如:singleton、prototype 等)
- • Bean 构造器参数(用于依赖注入)
- • Bean 属性设置(用于依赖注入)
- • Bean 自动绑定模式(如:通过名称 byName)
- • Bean 延迟初始化模式(延迟和非延迟)
- • Bean 初始化回调方法名称
- • Bean 销毁回调方法名称
基础使用
构建bean的BeanDefinition
的方式主要有两种:
- 1. 通过
BeanDefinitionBuilder
构建
public static void main(String[] args) {
// 创建一个Spring上下文对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 创建BeanDefinition构建器
BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Person.class);
definitionBuilder.addPropertyValue("name", "张三")
.addPropertyValue("age", 30);
// 获得BeanDefinition
BeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
// 将BeanDefinition注册到Spring上下文中
beanFactory.registerBeanDefinition("person", beanDefinition);
// 在执行getBean之前,即使注册到Spring上下文之后修改BeanDefinition值依旧生效
beanDefinition.getPropertyValues()
.add("age", 45);
Person person = beanFactory.getBean("person", Person.class);
System.out.println(person);
}
public static class Person {
private String name;
private String age;
// 。。。。 省略get、set和toString方法
}
输出结果:
Person{name='张三', age='45'}
- 1. 使用
AbstractBeanDefinition
派生类进行创建
public static void main(String[] args) {
// 创建一个Spring上下文对象
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 创建BeanDefinition派生类
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
// 设置对象相关元信息
genericBeanDefinition.setBeanClass(Person.class);
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("name", "王武")
.add("age", "33");
genericBeanDefinition.setPropertyValues(propertyValues);
// 将BeanDefinition注册到Spring上下文中
beanFactory.registerBeanDefinition("person", genericBeanDefinition);
// 从上下文获取Bean对象
Person person = beanFactory.getBean(Person.class);
System.out.println(person);
}
输出结果:
Person{name='王武', age='33'}
BeanDefinition
其实很简单,他其实本质就是用来描述一个对象的,他记录了一个对象的名称、属性、构造函数、生命周期等等。我们借助BeanDefinition
可以很简单的完整的创建出一个对象。
注册BeanDefinition
首先需要阐述一点,注册BeanDefinition
不等于实例化bean,因为BeanDefinition
元信息只能看作一个一个对象的组成部分,而实例化bean是需要有一个完整的BeanDefinition
之后才能完成的。就如同堆积木一样,一块块的积木就是一个元信息,而最后堆出来的完整样式才是实例化出来的数据。我们要拼出完整的积木首先要准备好所有的积木。
将对象信息注册到BeanDefinition
主要有如下几种方式:
- • XML配置元信息
<bean name="user" class="cn.phshi.entity.User">
<property name="name" value="张三"/>
<property name="age" value="33"/>
</bean>
<bean name="supperUser" class="cn.phshi.entity.SupperUser"
parent="user" scope="prototype" primary="true">
<property name="phone" value="13311111111"/>
</bean>
- • Java 注解配置元信息
- • @Bean
public class RegisterBeanDefinitionDemo {
public static void main(String[] args) {
// 获取上下文对象
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(RegisterBeanDefinitionDemo.class);
applicationContext.refresh();
Person person = applicationContext.getBean(Person.class);
System.out.println(person);
}
// 注册bean信息
@Bean
public Person person() {
// 创建了一个对象的同时其实也是把Person中class相关的元信息注册到容器中了
return new Person("张三", 32);
}
public static class Person {
private String name;
private Integer age;
// 。。。。 省略构造器,get/set方法喝toString方法
}
}
- • @Component 常见方式,该处不做例子
- • @Import
// 使用Import导入Config的配置信息
@Import(RegisterBeanDefinitionDemo.Config.class)
public class ImportBeanDefinitionDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(ImportBeanDefinitionDemo.class);
applicationContext.refresh();
RegisterBeanDefinitionDemo.Person bean = applicationContext.getBean(RegisterBeanDefinitionDemo.Person.class);
System.out.println(bean);
}
}
public class RegisterBeanDefinitionDemo {
// 在ImportBeanDefinitionDemo中使用Import导入了该对象
public static class Config{
// 依旧是使用Bean方式注入
@Bean
public Person person() {
return new Person("张三", 32);
}
}
public static class Person {
private String name;
private Integer age;
// 。。。。 省略构造器,get/set方法喝toString方法
}
}
- • Java API 配置元信息
- • BeanDefinitionRegistry:在上一章基础使用中将
BeanDefinitionBuilder
中有相关案例,案例中使用的beanfactory
是DefaultListableBeanFactory
,但是实际上DefaultListableBeanFactory
就是继承的BeanDefinitionRegistry
。 - • BeanDefinitionReaderUtils
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//Bean的定义信息
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Person.class)
.addPropertyValue("name", "王武")
.addPropertyValue("age", 23)
.getBeanDefinition();
// 注册到DefaultListableBeanFactory中
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, beanFactory);
Person bean = beanFactory.getBean(Person.class);
System.out.println(bean);
}
- • AnnotatedBeanDefinitionReader
- • SingletonBeanRegistry
Bean的命名
每个 Bean 拥有一个或多个标识符(identifiers),这些标识符在 Bean 所在的容器必须是唯一的。通常,一个 Bean 仅有一个标识符,如果需要额外的,可考虑使用别名(Alias)来 扩充。当然,Bean 的 id 或 name 属性并非必须制定,如果留空的话,容器会为 Bean 自动生成一个唯一 的名称。Bean 的命名尽管没有限制,不过官方建议采用驼峰的方式,更符合 Java 的命名约 定。
Spring中Bean名称生成器
Spring提供了一个接口BeanNameGenerator
来自定义实现Bean的名称生成。类图如下:
image-20230225181046807
他主要有三个实现:
- • DefaultBeanNameGenerator: 默认通用 BeanNameGenerator 实现;
- • AnnotationBeanNameGenerator: 基于注解扫描的 BeanNameGenerator 实现;
- • FullyQualifiedAnnotationBeanNameGenerator:
AnnotationBeanNameGenerator
的一个扩展,主要是为了解决具有相同名称但存在于不同包中的类而导致命名冲突的问题。
Bean 的别名
我们可以在bean的id和name之外,为bean再提供多个名称,这样设置的意义在于我们可以对一个bean进行场景化命名,也就是可以允许在不同场景使用不同的名称获取bean,但是最后获得的对象是一样的。
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-definitions-context.xml");
User user = beanFactory.getBean("user", User.class);
User yunNanUser = beanFactory.getBean("yunnan-user", User.class);
System.out.println("获得的两个对象是否相等:" + (user == yunNanUser));
}
public class User {
private String name;
private Integer age;
// 。。。。 省略get、set和toString方法
}
xml文件中的配置:
<bean name="user" class="cn.phshi.entity.User">
<property name="name" value="张三"/>
<property name="age" value="33"/>
</bean>
<alias name="user" alias="yunnan-user"/>
输出结果:
获得的两个对象是否相等:true
也可以使用@Bean的方式进行配置:
@Bean(name = {"user", "huansi-user"})
public User user() {
User user = new User();
user.setName("黄四");
user.setAge(20);
return user;
}
实例化Bean
实例化bean其实就是通过前面的BeanDefinition元信息来创建一个或者Bean的实例对象。
实例化Bean的方式有很多种,我们分别来看一下相关方式:
- • 通过构造器实例化:
通过构造器进行实例化的方式比较常见,通常情况有XML配置方式,@Bean,@Component等注解的配置方式等,这种方式创建对象上面也演示了很多,这里就不再次赘述了。
- • 通过静态工厂方法实例化:
静态工厂方法:
public final class UserStaticFactory {
private static User user = new User("张三",54);
private UserStaticFactory() {}
public static User userInstance() {
return user;
}
}
XML配置文件:
<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 静态方法实例化 Bean -->
<bean name="staticUser" class="cn.phshi.bean.factory.UserStaticFactory" factory-method="userInstance"/>
</beans>
- • 通过实工厂方法实例化:
实例方法:
public class UserInstanceFactory {
private static User user = new User("lisi",29);
public User userInstance() {
return user;
}
}
XML配置文件:
<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 实例化静态工厂 -->
<bean name="userInstanceFactory" class="cn.phshi.bean.factory.UserInstanceFactory"/>
<!-- 使用实例化工厂创建对象 -->
<bean name="user" factory-bean="userInstanceFactory" factory-method="userInstance"/>
</beans>
- • 通过FactoryBean方式实例化:
FactoryBean对象:
public class UserFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new User("王五", 22);
}
@Override
public Class<?> getObjectType() {
return User.class;
}
}
XML配置文件:
<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- FactoryBean实例化 Bean -->
<bean name="factory-bean-user" class="cn.phshi.bean.factory.UserFactoryBean"/>
</beans>
初始化Bean
主要用于Bean创建时候做一些初始化操作
初始化Bean的方法有:
- • @PostConstruct注解:
public class Person {
private String name;
private Integer age;
// 改方法声明PostConstruct,将在创建Bean时候执行改方法
@PostConstruct
public void postConstruct() {
System.out.println("@PostConstruct方法执行~~~");
}
// 省略构造器、get/set、toString方法
}
- • 实现 InitializingBean 接口的 afterPropertiesSet() 方法
// 实现 InitializingBean 接口
public class Person implements InitializingBean {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println(" InitializingBean 接口的 afterPropertiesSet() 方法");
}
// 省略构造器、get/set、toString方法
}
- • 自定义初始化方法
- • 使用@Bean中initMethod定义的初始化方法
@Bean(initMethod = "initMethod")
public Person person() {
return new Person("张三", 33);
}
- • xml配置的方式
<bean name="user" class="cn.phshi.entity.User" init-method="initMethod">
<property name="name" value="张三"/>
<property name="age" value="33"/>
</bean>
- • AbstractBeanDefinition#setInitMethodName(String)方式注入
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Person.class)
.addPropertyValue("name", "王武")
.addPropertyValue("age", 23)
// 使用BeanDefinition构建Bean的元信息
.setInitMethodName("initMethod")
.getBeanDefinition();
三种方式同时使用的执行顺序:
@PostConstruct -> InitializingBean -> @Bean
延迟初始化Bean
延迟初始化的方式:
- • Java 注解:@Lazy(true)
- • XML 配置:<bean lazy-init=”true” ... />
销毁Bean
和初始化相仿,主要作用是在Bean摧毁的时候完成Bean的资源释放操作。
Bean的摧毁方式有(由于和初始化相仿,这里不做示例):
- • @PreDestroy 标注方法
- • 实现 DisposableBean 接口的 destroy() 方法
- • 自定义销毁方法
- • Java 注解:@Bean(destroy=”destroy”)
- • XML 配置:<bean destroy=”destroy” ... />
- • Java API:AbstractBeanDefinition#setDestroyMethodName(String)
参考文章
什么是 BeanDefinition?