1、MutablePropertyValues 概述
其实在绝大多情况下,MutablePropertyValues 这个类很少用,但是涉及到框架改造扩展可能就要使用到这个类。并且这个类在 BeanDefinition 模板中也是一个非常重要的角色。
id:Bean 唯一标识名称。
beanClass:类全限定名(包名+类名)。
init-method:定义 Bean 初始化方法,Bean 组装之后调用,必须是一个无参数方法。
destory-method:定义 Bean 销毁方法,在 BeanFactory 关闭时触发,同样也必须是一个无参构造方法,只能应用于 SingletonBean 单例 Bean。
factory-method:定义创建 Bean 对象的工厂方法,用于下面的 factory-bean,表示这个 Bean 是通过工厂方法创建,此时,class 属性 “失效”。
factory-bean:定义创建该 Bean 的工厂类,如果使用了 factory-bean,则 class 属性相当于 “失效”。
MultablePropertyValues:用于封装类属性的集合,里面是一个 List 容器,包装了很多 PropertyValue ,一个 PropertyValue 封装了一个属性及其对应的值,可以说一个属性及其值就是一个 PropertyValue,当我们需要再 BeanDefinition 中修改某个类里面的属性时就可以使用该类。
ConstructorArgumentValues:用来在 BeanDefinition 模版中指定使用哪个构造方法进行实例化 Bean,这个参数在集成 MyBatis 框架就使用到了。
那么这个类有什么作用呢?
目前据我了解到的这个类可以帮助我们在 BeanDefinition 中修改某个类的属性,下面就举个案例说明。
2、代码演示
先定义 ProcessorEntity 实体类,类里面有两个属性:name、birthday,两个构造函数,如下:
public class ProcessorEntity {
private String name = "ABC";
private Integer birthday;
@Autowired
public ProcessorEntity(Integer birthday) {
System.out.println("birthdaybirthdaybirthdaybirthday");
}
public ProcessorEntity(String name) {
System.out.println("namenamenamenamenam");
}
public Integer getBirth() {
return birthday;
}
public void setBirth(Integer birthday) {
this.birthday = birthday;
}
public String getName() {
System.out.println("getName() 方法中的 scannerEntity = " + scannerEntity);
return name;
}
public void setName(String name) {
this.name = name;
}
}
ProcessorEntity 类我们通过 BeanDefinitionRegistryPostProcessor 手动注册,这样也可以比较方便的对 BeanDefinition 进行修改。
定义一个 MyConfigurationPostProcessor1 类获取到 BeanDefinition 定义,可以借助 BeanDefinitionRegistryPostProcessor 接口,如下:
@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(ProcessorEntity.class);
MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
// 修改 ProcessorEntity 类中 name 属性默认值
propertyValues.addPropertyValue("name","小明");
// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
}
}
代码中通过自己定制一个 GenericBeanDefinition,然后注册到 Spring 容器中,这样 Spring 就会按照设定好的模版生产 bean。
通过代码 genericBeanDefinition.getPropertyValues()
可以获取到 MutablePropertyValues 集合,源码如下:
容器拿到之后,假设现在要对 ProcessorEntity 类的属性 name 赋值,或者说是修改,应该怎么做呢?
可以将属性 name 和要设置的值封装成 PropertyValue,然后添加到 MutablePropertyValues 容器中即可,这样 Spring 自动帮咱们实现属性的设置。
其中代码 propertyValues.addPropertyValue("name","小明")
就是将 name=小明 封装到 PropertyValue,并添加到 MutablePropertyValues 容器,可以从源码看出,如下:
最后测试结果如下:
public class TestBeanScanner {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ScannerConfig.class);
ProcessorEntity processorEntity = context.getBean("processorEntity", ProcessorEntity.class);
System.out.println("processorEntity = " + processorEntity+",name="+processorEntity.getName1());
}
}
结果如下:
processorEntity = com.gwm.bean221207.processors.ProcessorEntity@20ccf40b,name=小明
从结果可以发现,name 默认值是 “ABC”,最终被修改成了 “小明”。
name 属性修改完成了,现在又想将 ProcessorEntity 类另一个属性 birthday 也修改下,我们肯定以为那还不简单,直接写代码,如下所示:
@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(ProcessorEntity.class);
MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
// 修改 ProcessorEntity 类中 name 属性默认值
propertyValues.addPropertyValue("name","小明");
// 修改 ProcessorEntity 类中 birthday 属性默认值
propertyValues.addPropertyValue("birthday",2022);
// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
}
}
测试如下:
public class TestBeanScanner {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ScannerConfig.class);
ProcessorEntity processorEntity = context.getBean("processorEntity", ProcessorEntity.class);
System.out.println("processorEntity = " + processorEntity+",name="+processorEntity.getName());
System.out.println("processorEntity = " + processorEntity+",birthday="+processorEntity.getBirth());
}
}
结果如下:
发现抛出异常,奇怪,检查上面的代码,发现属性名称是叫做 birthday,GenericBeanDefinition 也确实是为 birthday 属性赋值了呀,应该没问题的,但是细心的朋友应该发现了,name 和 birthday 两个属性,name 的 getXxx() 、setXxx() 中 Xxx 和 name 的驼峰写法一样,但是 birthday 驼峰和 birth 完全不一样。
说到这儿了,大家应该能推测出,这个 MutablePropertyValues 的更新操作应该是调用了 setXxx() 方法去实现的,那么最好的验证就是在 setName() 方法里面打上端点 debug ,如下:
看调用栈及参数显示,可以发发现最终是调用 setName() 方法进行属性操作的,所以在 GenericBeanDefinition 中的 MutablePropertyValues 容器中属性名称要和 setXxx() 中的 Xxx 一样。所以我们需要将 MutablePropertyValues 中的 birthday 修改成和 setBirth() 方法中的 birth 一样,如下:
@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(ProcessorEntity.class);
MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
// 修改 ProcessorEntity 类中 name 属性默认值
propertyValues.addPropertyValue("name","小明");
propertyValues.addPropertyValue("birth",2022);
// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
}
}
其实继续往深处源码看,会发现,它是先收集本类中所有方法,然后把 set、和 get 截取掉,截取处理之后,首字母转换成小写,最终将这个方法名称作为 key 封装成一个个的 PropertyInfo 如下:
所以我们在 MutablePropertyValues 中添加的名称,如果在 ProcessorEntity 类中能够找到一个对应的 setXxx() 方法,那么这个方法就会被调用,现在给 ProcessorEntity 添加一个方法 setSex() 方法,但是类中并没有 sex 这个属性,如下:
public class ProcessorEntity {
private String name = "ABC";
private Integer birthday;
@Autowired
public ProcessorEntity(Integer birthday) {
System.out.println("birthdaybirthdaybirthdaybirthday");
}
public ProcessorEntity(String name) {
System.out.println("namenamenamenamenam");
}
public Object setSex(String sex) {
System.out.println("sex ====>"+sex);
return new Object();
}
public Integer getBirth() {
return birthday;
}
public void setBirth(Integer birthday) {
this.birthday = birthday;
}
public String getName() {
System.out.println("getName() 方法中的 scannerEntity = " + scannerEntity);
return name;
}
public void setName(String name) {
this.name = name;
}
}
然后再 MutablePropertyValues 中调用并赋值,如下:
@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(ProcessorEntity.class);
MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
// 修改 ProcessorEntity 类中 name 属性默认值
propertyValues.addPropertyValue("name","小明");
propertyValues.addPropertyValue("birth",2022);
propertyValues.addPropertyValue("sex","女");
// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
}
}
测试如下:
public class TestBeanScanner {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ScannerConfig.class);
ProcessorEntity processorEntity = context.getBean("processorEntity", ProcessorEntity.class);
System.out.println("processorEntity = " + processorEntity+",name="+processorEntity.getName());
System.out.println("processorEntity = " + processorEntity+",birthday="+processorEntity.getBirth());
}
}
发现 setSex() 被调用了,结果也正常输出。如下:
sex ====>女
processorEntity = com.gwm.bean221207.processors.ProcessorEntity@32b260fa,name=abcdddddd
processorEntity = com.gwm.bean221207.processors.ProcessorEntity@32b260fa,birthday=2022
3、ConstructorArgumentValues 指定构造方法实例化 Bean
上面讲完了 MutablePropertyValues 类的作用,现在继续讲解下 ConstructorArgumentValues 的作用,这个作用就非常清晰了,就是可以再 BeanDefinition 创建的时候,指定使用哪个构造方法实例化 bean。
开局我们在 ProcessorEntity 类中准备好了两个构造方法,一个参数是 String,一个是 Integer,现在指定用 Integer 参数的构造方法实例化 bean,代码如下:
@Component
public class MyConfigurationPostProcessor1 implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(ProcessorEntity.class);
MutablePropertyValues propertyValues = genericBeanDefinition.getPropertyValues();
// 修改 ProcessorEntity 类中 name 属性默认值
propertyValues.addPropertyValue("name","小明");
propertyValues.addPropertyValue("birth",2022);
propertyValues.addPropertyValue("sex","女");
ConstructorArgumentValues constructorArgumentValues = genericBeanDefinition.getConstructorArgumentValues();
constructorArgumentValues.addGenericArgumentValue(18);
// 然后再将 BeanDefinition 注册到 BeanFactory 容器中
registry.registerBeanDefinition("processorEntity",genericBeanDefinition);
}
}
通过 genericBeanDefinition.getConstructorArgumentValues()
代码可以获取到 ConstructorArgumentValues 容器,源码如下:
其实也是一个 List 容器,需要使用哪个构造方法,就对应将入参值添加到对应的参数上即可,constructorArgumentValues.addGenericArgumentValue(18)
源码如下:
我们传入给构造方法参数的值被封装成一个个 ValueHolder 对象,并被添加到了 ConstructorArgumentValues 容器中。调用的源码如下:
注意这里有个判断条件 mbd.hasConstructorArgumentValues() 这里肯定是成立,因为在 MyConfigurationPostProcessor1 类中已经添加参数值。