BeanDefinition
BeanDefinition里面放着Spring创建bean的过程中所需要的一切原料!
作用
- 提升效率:Spring创建一个类是通过反射创建的,创建类的时候需要一些创建信息,比如Class,比如注解信息等等,事先将这些信息缓存起来,在创建bean的时候能够直接从缓存中获取从而达到提升创建效率的目的。
- 方便修改:spring创建对象的时候,创建的信息全部是通过 BeanDefinition 内存储的信息来创建对象的,所以,我们可以通过修改BeanDefinition内部特定的值来改变Spring创建对象的结果!
- 方便扩展:我们通过一些特定的接口,可以获取到一个类的所有的BeanDefinition信息,从而完成一些特定功能的实现!
番外
扩展点:简单了解一下后面学~
BeanFactoryPostProcessor(工厂后处理器)
BeanPostProcessor(Bean后处理器)
开发时怎么像Spring一样预留扩展点呢?领导老说开个口子以防万一,那么什么是口子?我太菜了不懂~
就比如SpringIOC,加载好BeanDefinition之后,都会调用一个空方法
postProcessBeanFactory
,入参是应用上下文beanFactory
,开发人员实现这个空方法(接口)之后,就可以在空方法里面写自己的代码逻辑,而且入参也提供了所需的数据(或者保存数据的Map),我们就可以灵活的修改数据或者心中数据,以实现自己的特殊需求
源码
注意:并不是标准的源码!有些方法是为了看起来方便加上去的!
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
作用域相关
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
void setScope(String scope);
String getScope();
boolean isSingleton();
boolean isPrototype();
角色类型相关:主要用于区别用户自定义bean还是框架内部bean
int ROLE_APPLICATION = 0; 表示这个 Bean 是用户自己定义的 Bean
int ROLE_SUPPORT = 1; 表示这个 Bean 是某些复杂配置的支撑部分
int ROLE_INFRASTRUCTURE = 2; 表示这是一个 Spring 内部的 Bean
int getRole();
配置的父类关系
void setParentName(String parentName);
String getParentName();
bean的全限定名
void setBeanClassName(String beanClassName);
String getBeanClassName();
是否懒加载
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
当前bean所依赖的其他bean(使用depends-on属性指定的Bean要先初始化完毕后才初始化当前Bean)
void setDependsOn(String... dependsOn);
String[] getDependsOn();
Candidate:候选者,候选者命名规则
表示当前类是否可以作为一个注入候选者注入到别的bean中
如果出现NoUniqueBeanDefinitionException异常的原因:没有设置primary=“ture”且所有的候选者autowireCandidate=“true”
void setAutowireCandidate(boolean autowireCandidate);
boolean isAutowireCandidate();
是否主要的首选的(当一个接口有多个实现时,且通过btType的形式注入时,会出现NoUniqueBeanDefinitionException异常,此时需要指定一个实现为主要bean,注入就会只注入primary=“true”的)
void setPrimary(boolean primary);
boolean isPrimary();
指定用于创建当前bean的工厂bean名称和工厂方法名称
用于获取已经设置的工厂bean名称和工厂方法名称
void setFactoryBeanName(String factoryBeanName);
String getFactoryBeanName();
void setFactoryMethodName(String factoryMethodName);
String getFactoryMethodName();
Bean 构造方法的参数,用于实例化 Bean 判断
AbstractBeanDefinition类中:hasConstructorArgumentValues方法,判断是否有带参构造器。
ConstructorArgumentValues getConstructorArgumentValues();
Bean 属性列表
MutablePropertyValues getPropertyValues();
是否抽象的
boolean isAbstract();
描述信息 类似订单的备注
String getDescription();
资源描述 还是描述信息 类似订单的商家备注
String getResourceDescription();
BeanDefinition getOriginatingBeanDefinition();
Bean 初始化方法 @Bean(initMethod="init")来指定初始化方法 类似 @PostConstruct
void setInitMethodName(@Nullable String initMethodName);
String getInitMethodName();
Bean 销毁方法 @Bean(destroyMethod = "") 类似 @PreDestory
void setDestroyMethodName(@Nullable String destroyMethodName);
String getDestroyMethodName();
}
源码说明
1. parentName 说明
ChildBeanDefinition实现类中有这个属性,说明 BeanDefinition 存在父子继承的关系 即类似类里的继承关系 extends
<bean id="parentBean" class="com.example.ParentBean"> <!--父亲-->
<property name="stringProperty" value="stringValue"/>
</bean>
<bean id="childBean" parent="parentBean"> <!--儿子-->
<property name="anotherStringProperty" value="anotherStringValue"/>
</bean>
2. depends-on 说明1
当前bean所依赖的其他bean(使用depends-on属性指定的Bean要先初始化完毕后才初始化当前Bean)
<bean id="helloApi" class="com.feng.spring.chapter2.helloworld.HelloApi">
</bean>
<bean id="decorator" class="cn.javass.spring.chapter3.bean.HelloApiDecorator"
depends-on="helloApi">
<property name="helloApi"><ref bean="helloApi"/></property>
</bean>
3. autowireCandidate 说明2
Candidate候选者命名规则
:Candidate(候选者)表示当前类是否可以作为一个注入候选者
注入到别的bean中!
- void setAutowireCandidate(boolean autowireCandidate);
- boolean isAutowireCandidate();
如果出现NoUniqueBeanDefinitionException异常:没有设置primary=“ture”
且所有的候选者autowireCandidate=“true”
4. setFactoryBeanName 说明
setFactoryBeanName 和 setFactoryMethodName 在 Spring 框架中用于指定通过哪个工厂bean和工厂方法来创建bean实例。
它们在复杂bean创建逻辑、依赖其他bean的状态或方法的场景中非常有用。
在 Spring 源码中,它们的作用是在bean创建过程中,通过调用指定的工厂方法来创建bean实例。
<!-- 定义工厂bean -->
<bean id="myFactoryBean" class="com.example.MyFactoryBean"/>
<!-- 使用工厂bean来创建其他bean -->
<bean id="myBean" factory-bean="myFactoryBean" factory-method="createInstance"/>
class MyBean {
// Bean的具体实现
}
package com.example;
public class MyFactoryBean {
public MyBean createInstance() {
return new MyBean();
}
}
Spring源码中创建实体类的时候又是怎么使用factory-bean
、factory-method
的?
答案: AbstractAutowireCapableBeanFactory 类:这个类负责bean的创建和初始化。以下是其中的一些关键代码段:
protected Object createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// ...省略部分代码...
// 如果存在工厂bean名称和工厂方法名称,则通过工厂方法创建bean
if (mbd.getFactoryBeanName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// ...省略部分代码...
}
instantiateUsingFactoryMethod 方法:这个方法使用工厂方法来实例化bean:
protected Object instantiateUsingFactoryMethod(
final String beanName, final RootBeanDefinition mbd, @Nullable final Object[] explicitArgs) {
// ...省略部分代码...
// 获取工厂bean
Object factoryBean = getBean(mbd.getFactoryBeanName());
// 获取工厂方法
Method factoryMethod = findFactoryMethod(mbd, factoryBean);
// 调用工厂方法创建bean实例
return invokeFactoryMethod(beanName, mbd, factoryBean, factoryMethod, explicitArgs);
}
5. setConstructorArgumentValues 说明
把构造函数相关的配置信息,存储到bean定义的ConstructorArgumentValues
对象中:
package com.example;
public class MyBean {
private String name;
private int age;
public MyBean(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "MyBean{name='" + name + "', age=" + age + '}';
}
}
通过编程方式配置BeanDefinition
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.f87i
public class ConstructorArgumentExample {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 定义BeanDefinition并设置构造函数参数
BeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(MyBean.class)
.getBeanDefinition();
ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addGenericArgumentValue("Test Bean"); // 第一个参数,按类型匹配
args.addGenericArgumentValue(30); // 第二个参数,按类型匹配
beanDefinition.getConstructorArgumentValues().addArgumentValues(args);
// 注册BeanDefinition
beanFactory.registerBeanDefinition("myBean", beanDefinition);
// 获取bean实例
MyBean myBean = beanFactory.getBean("myBean", MyBean.class);
System.out.println("Bean instance: " + myBean);
// 验证构造函数参数
System.out.println("是否存在有参构造函数:" + beanDefinition.hasConstructorArgumentValues());
System.out.println("Constructor Arguments: " + beanDefinition.getConstructorArgumentValues().getGenericArgumentValues());
}
}
Bean instance: MyBean{name='Test Bean', age=30}
是否存在有参构造函数:true
构造函数参数: [ValueHolder{value=Test Bean, type=null, name=null, source=null}, ValueHolder{value=30, type=null, name=null, source=null}]
[
ValueHolder{
value=TestBean, 构造函数参数的值
type=null, 构造函数参数的类型
name=null, 构造函数参数的名称
source=null 参数值的来源
},
ValueHolder{
value=30,
type=null,
name=null,
source=null
}
]
AbstractAutowireCapableBeanFactory 类:在创建bean实例时,Spring容器会检查bean定义是否包含构造函数参数:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 省略部分代码...
// 如果定义中包含构造函数参数,则使用带参数的构造函数创建bean实例
if (mbd.hasConstructorArgumentValues() || args != null) {
return autowireConstructor(beanName, mbd, ctorToUse, argsToUse);
}
// 省略部分代码...
}
autowireConstructor 方法:Spring怎么选择构造函数的?3
protected BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, Constructor<?>[] chosenCtors, Object[] explicitArgs) {
// 省略部分代码...
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
// 使用cargs中的参数来选择合适的构造函数并实例化bean
// 省略部分代码...
}
- 存在无参构造函数,且没有 @Autowired注解时使用无参构造器。
- 不存在无参构造函数,但存在有参构造函数,并且没有@Autowired注解会报错,添加@Autowired注解就不会报错。
- 存在多个构造函数,并且含有无参构造函数,有@Autowired注解 会使用@Autowired注解标注的构造器。
- 有多个@Autowired注解会报错,除非全设置为required = false,此时会加载参数最多的构造器。
从上面来看:首先就是看能拿到那些配置信息,如果cargs
里面都没有有参构造,那Spring肯定就是选择使用无参构造来初始化对象!如果cargs
里面有内容,那就看有几个内容,从而选择符合参数数量的构造函数,再结合@Autowired
等等…
5.1 ConstructorArgumentValues 类 4
ConstructorArgumentValues:
类用于管理bean定义中的构造函数参数,可以按索引或通用方式添加和获取参数。
ValueHolder内部类:
封装了构造函数参数的具体信息,包括值、类型、名称和来源。
<bean id="book0" class="demo3.school.Book">
<constructor-arg name="writer" index="0" type="java.lang.String" value="我爱中国"/>
<constructor-arg name="isbn" index="1" type="long" value="18888888"/>
<constructor-arg name="owner" index="2" type="demo3.school.Student" ref="student1"/>
</bean>
XML配置文件中的信息都记录到ConstructorArgumentValues
中以便Spring使用:
[
ValueHolder{
value=我爱中国, 构造函数参数的值
type=java.lang.String, 构造函数参数的类型
name=writer, 构造函数参数的名称
source=null 参数值的来源
},
ValueHolder{
value=18888888,
type=long,
name=isbn,
source=null
},
ValueHolder{
value=student1,
type=demo3.school.Student,
name=owner,
source=null
}
]
6. getPropertyValues() 说明
该bean定义的一组属性值,这些值会在bean实例化后注入到bean的相应属性中!
6.1 MutablePropertyValues类
MutablePropertyValues类用于存储和管理一组属性值。
主要功能
- 添加属性值:可以向MutablePropertyValues对象中添加新的属性值。
- 修改属性值:可以修改现有的属性值。
- 删除属性值:可以删除现有的属性值。
不需要深究,只需要知道它主要的功能即可,就是一个List,然后对这个List添加修改删除…
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MutablePropertyValues {
// 存储PropertyValue对象的列表
private final List<PropertyValue> propertyValueList;
// 无参构造函数,初始化空的propertyValueList
public MutablePropertyValues() {
this.propertyValueList = new ArrayList<>();
}
// 构造函数,通过给定的PropertyValues对象初始化propertyValueList
public MutablePropertyValues(PropertyValues original) {
if (original != null) {
this.propertyValueList = new ArrayList<>(Arrays.asList(original.getPropertyValues()));
} else {
this.propertyValueList = new ArrayList<>();
}
}
// 添加或更新PropertyValue对象到propertyValueList中
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
for (int i = 0; i < this.propertyValueList.size(); i++) {
PropertyValue currentPv = this.propertyValueList.get(i);
// 如果属性名称相同,则更新现有的PropertyValue对象
if (currentPv.getName().equals(pv.getName())) {
this.propertyValueList.set(i, pv);
return this;
}
}
// 如果属性名称不存在,则添加新的PropertyValue对象
this.propertyValueList.add(pv);
return this;
}
// 通过属性名称和属性值添加PropertyValue对象到propertyValueList中
public MutablePropertyValues add(String propertyName, Object propertyValue) {
addPropertyValue(new PropertyValue(propertyName, propertyValue));
return this;
}
// 获取所有PropertyValue对象的数组
public PropertyValue[] getPropertyValues() {
return this.propertyValueList.toArray(new PropertyValue[0]);
}
// 根据属性名称获取PropertyValue对象
public PropertyValue getPropertyValue(String propertyName) {
for (PropertyValue pv : this.propertyValueList) {
if (pv.getName().equals(propertyName)) {
return pv;
}
}
return null;
}
// 检查是否包含指定属性名称的PropertyValue对象
public boolean contains(String propertyName) {
return getPropertyValue(propertyName) != null;
}
// 根据属性名称删除PropertyValue对象
public MutablePropertyValues removePropertyValue(String propertyName) {
this.propertyValueList.removeIf(pv -> pv.getName().equals(propertyName));
return this;
}
// 检查propertyValueList是否为空
public boolean isEmpty() {
return this.propertyValueList.isEmpty();
}
}
6.2 PropertyValue 类
PropertyValue类用于封装单个属性值,包含属性的名称和对应的值。
public class PropertyValue {
// 属性的名称
private final String name;
// 属性的值
private final Object value;
// 构造函数,初始化属性名称和属性值
public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}
// 获取属性名称
public String getName() {
return this.name;
}
// 获取属性值
public Object getValue() {
return this.value;
}
}
使用案例:
public class PropertyValueExample {
public static void main(String[] args) {
// 创建PropertyValue对象,属性名称为"name",属性值为"Test Bean"
PropertyValue pv = new PropertyValue("name", "Test Bean");
// 获取并打印属性名称
System.out.println("Property Name: " + pv.getName());
// 获取并打印属性值
System.out.println("Property Value: " + pv.getValue());
}
}
6.3 Spring源码使用场景
初始化对象后为对象属性值赋值:属性值可以是简单类型,也可以是引用其他bean、集合元素等。因此,需要解析这些属性值,并将解析后的值注入到bean实例中。
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionValueResolver;
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
// 初始化mpvs变量
MutablePropertyValues mpvs = null;
// 检查pvs是否是MutablePropertyValues的实例
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
} else {
// 如果不是,创建一个新的MutablePropertyValues实例
mpvs = new MutablePropertyValues(pvs);
}
// 创建一个BeanDefinitionValueResolver实例,用于解析属性值中的引用等
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, bw);
// 遍历所有的PropertyValue对象
for (PropertyValue pv : mpvs.getPropertyValues()) {
// 获取属性名称
String propertyName = pv.getName();
// 获取原始的属性值
Object originalValue = pv.getValue();
// 解析属性值(可能是引用、集合元素等)
Object resolvedValue = valueResolver.resolveValueIfNecessary(propertyName, originalValue);
// 使用BeanWrapper将解析后的值设置到bean实例中
bw.setPropertyValue(propertyName, resolvedValue);
}
}
6.4 番外:简单的认识一下BeanWrapper
- 在初始化bean的过程中,
BeanWrapper
用于封装bean的实例。比如我们的JAVA实体类,只有getter、setter方法,Spring怎么知道调用setName、还是setSex。这时候就需要BeanWrapper
提供的方法+Bean定义提供的元信息,从而实现为属性赋值的功能,如下:
案例:使用BeanWrapper设置和获取bean属性
public class MyBean {
private String name;
private int age;
// getters and setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.MutablePropertyValues;
public class BeanWrapperExample {
public static void main(String[] args) {
// 创建一个MyBean实例
MyBean myBean = new MyBean();
// 使用BeanWrapper封装MyBean实例
BeanWrapper beanWrapper = new BeanWrapperImpl(myBean);
// 设置bean的属性
beanWrapper.setPropertyValue("name", "Test Bean");
beanWrapper.setPropertyValue("age", 30);
// 获取并打印bean的属性
System.out.println("Bean Name: " + beanWrapper.getPropertyValue("name"));
System.out.println("Bean Age: " + beanWrapper.getPropertyValue("age"));
// 使用MutablePropertyValues批量设置属性
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("name", "Updated Bean");
propertyValues.add("age", 35);
beanWrapper.setPropertyValues(propertyValues);
// 再次获取并打印bean的属性
System.out.println("Updated Bean Name: " + beanWrapper.getPropertyValue("name"));
System.out.println("Updated Bean Age: " + beanWrapper.getPropertyValue("age"));
}
}
6.5 番外:Environment
、@Value
和MutablePropertyValues
MutablePropertyValues 只要是需要记录属性值:key-value形式的,都可以用他,他就是List,里面一个PropertyValue
就是具体的Entity<key,value>
引用
什么是BeanDefinition!
想要学会Spring源码,你必知必会的BeanDefinition原理!
【Spring】BeanDefinition 源码深度解析