在日常的工作中,我们经常遇到一些特殊的案例需要自定义属性的解析器来完成对应的属性解析工作,大家需要理解它的本质来进行随意的扩展工作,简单的举个例子,下面的bean标签,有个address字段,在spring进行属性填充的时候把value按照下划线进行截取分割转换为一个address省市区三级对象,这个就需要我们自定义属性编辑器。先说用法再说原理:
1.xml文件新增两个bean标签,后续spring启动加载配置文件会对这两个bean对象进行实例化,属性填充,初始化等一系列操作,CustomEditorConfigurer实现了BeanFactoryPostProcessor接口,后续在执行invokeBfpp的时候会调用postProcessBeanFactory方法,然后调用beanFactory的addPropertyEditorRegistrar把下面的list的AddressPropertyEditorRegistrar添加到beanFactory的属性字段propertyEditorRegistrars中
<bean id="customer" class="com.mashibing.selfEditor.Customer">
<property name="name" value="zhangsan"></property>
<property name="address" value="河北省_邯郸市_武安市"></property>
</bean>
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="com.mashibing.selfEditor.AddressPropertyEditorRegistrar"></bean>
</list>
</property>
</bean>
public class Address {
private String province;
private String city;
private String town;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getTown() {
return town;
}
public void setTown(String town) {
this.town = town;
}
@Override
public String toString() {
return "Address{" +
"province='" + province + '\'' +
", city='" + city + '\'' +
", town='" + town + '\'' +
'}';
}
}
上面第一个customer标签
public class Customer {
private String name;
private Address address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
}
自定义一个实现了PropertyEditorSupport接囗的编辑器,在进行属性填充会调用set方法对custom的address字段进行截取然后放在这个对象里
public class AddressPropertyEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
String[] s = text.split("_");
Address address = new Address();
address.setProvince(s[0]);
address.setCity(s[1]);
address.setTown(s[2]);
this.setValue(address);
}
}
让spring能够识别到此编辑器,自定义实现一个属性编辑器的注册器,实现了PropertyEditorRegistrar接口
public class AddressPropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Address.class,new AddressPropertyEditor());
}
}
prepareBeanFactory在执行的时候会向beanfactory添加一个
为beanFactory增加一个默认的propertyEditor,这个主要是对bean的属性等设置管理的一个工具类 实体类的参数解析工具类放在spring 后续可以按需使用的
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
beanFactory.addPropertyEditorRegistrar的实现细节为:
@Override
public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
this.propertyEditorRegistrars.add(registrar);
}
调用链路
prepareBeanFactory:745, AbstractApplicationContext (org.springframework.context.support)
refresh:581, AbstractApplicationContext (org.springframework.context.support)
<init>:150, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:98, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:15, MyClassPathXmlApplicationContext (com.mashibing)
main:84, Test (com.mashibing)
这里可以看到beanmap里有xml文件的两个标签定义信息
找到所有实现BeanFactoryPostProcessor接口的类,放到postProcessorNames数组
对实现BeanFactoryPostProcessor接口的类进行创建,这里会创建CustomEditorConfigurer对象
// 遍历postProcessorNames,将BeanFactoryPostProcessor按实现PriorityOrdered、实现Ordered接口、普通三种区分开
for (String ppName : postProcessorNames) {
// 跳过已经执行过的BeanFactoryPostProcessor
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
// 添加实现了PriorityOrdered接口的BeanFactoryPostProcessor到priorityOrderedPostProcessors
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
// 添加实现了Ordered接口的BeanFactoryPostProcessor的beanName到orderedPostProcessorNames
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
// orderedPostProcessorNames.add(ppName);
orderedPostProcessor.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
} else {
// 添加剩下的普通BeanFactoryPostProcessor的beanName到nonOrderedPostProcessorNames
// nonOrderedPostProcessorNames.add(ppName);
nonOrderedPostProcessorNames.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
}
这里是属性填充方法,可以看出CustomEditorConfigurer对象需要的属性是AddressPropertyEditorRegistrar对象
因为AddressPropertyEditorRegistrar对象是引用数据类型这里也要对AddressPropertyEditorRegistrar进行创建之后再放入CustomEditorConfigurer的propertyEditorRegistrars数组中
上面两个对象创建完成之后,CustomEditorConfigurer就是个完成的对象,属性也填充完毕了
// 遍历普通的BeanFactoryPostProcessor,执行postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(nonOrderedPostProcessorNames, beanFactory);
/**
* 调用给定的 BeanFactoryPostProcessor类型Bean对象
*
* Invoke the given BeanFactoryPostProcessor beans.
*/
private static void invokeBeanFactoryPostProcessors(
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
//遍历postProcessors
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
//回调 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法,使得每个postProcessor对象都可以对
// beanFactory进行调整
postProcessor.postProcessBeanFactory(beanFactory);
}
}
这里调用CustomEditorConfigurer的postProcessBeanFactory方法把实现了PropertyEditorRegistrar接口的AddressPropertyEditorRegistrar放进去
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 如果属性编辑注册器不等于空
if (this.propertyEditorRegistrars != null) {
// 遍历属性编辑注册器的集合
for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
// 将属性编辑注册器添加到beanFactory
beanFactory.addPropertyEditorRegistrar(propertyEditorRegistrar);
}
}
// 如果自定义编辑器不等于空
if (this.customEditors != null) {
// 遍历自定义编辑器集合将自定义编辑器添加到beanFactory中
this.customEditors.forEach(beanFactory::registerCustomEditor);
}
}
这个map里面存放的就是xml文件配置的两个bean标签,关于bean标签如何放到这个map的如何解析的 请看这个:Spring源码-xml配置文件如何加载解析默认标签变为BeanDefinition
CustomEditorConfigurer这个对象再上述创建成功了,放入一级缓存了。
propertyEditorRegistrars集合存放的是实现了PropertyEditorRegistrar接口的对象,现在有两个:
现在开始customer对象的创建,链路如下:
registerCustomEditors:12, AddressPropertyEditorRegistrar (com.mashibing.selfEditor)
registerCustomEditors:1725, AbstractBeanFactory (org.springframework.beans.factory.support)
initBeanWrapper:1690, AbstractBeanFactory (org.springframework.beans.factory.support)
instantiateBean:1609, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBeanInstance:1495, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:675, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:630, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:417, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 283717519 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$8)
getSingleton:370, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:414, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:260, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:993, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:1024, AbstractApplicationContext (org.springframework.context.support)
refresh:614, AbstractApplicationContext (org.springframework.context.support)
<init>:150, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:98, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:15, MyClassPathXmlApplicationContext (com.mashibing)
main:84, Test (com.mashibing)
下面都是属性填充的关键步骤:
读取到address的value:河北省_邯郸市_武安市
关键方法:
if (pvs != null) {
//应用给定的属性值,解决任何在这个bean工厂运行时其他bean的引用。必须使用深拷贝,所以我们 不会永久地修改这个属性
applyPropertyValues(beanName, mbd, bw, pvs);
}
//如果可转换
if (convertible) {
//将resolvedValue转换为指定的目标属性对象
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
setAsText:9, AddressPropertyEditor (com.mashibing.selfEditor)
doConvertTextValue:592, TypeConverterDelegate (org.springframework.beans)
doConvertValue:554, TypeConverterDelegate (org.springframework.beans)
convertIfNecessary:194, TypeConverterDelegate (org.springframework.beans)
convertIfNecessary:588, AbstractNestablePropertyAccessor (org.springframework.beans)
convertForProperty:607, AbstractNestablePropertyAccessor (org.springframework.beans)
convertForProperty:219, BeanWrapperImpl (org.springframework.beans)
convertForProperty:2205, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
applyPropertyValues:2143, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
populateBean:1793, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
doCreateBean:734, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
createBean:630, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
lambda$doGetBean$0:417, AbstractBeanFactory (org.springframework.beans.factory.support)
getObject:-1, 283717519 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$8)
getSingleton:370, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support)
doGetBean:414, AbstractBeanFactory (org.springframework.beans.factory.support)
getBean:260, AbstractBeanFactory (org.springframework.beans.factory.support)
preInstantiateSingletons:993, DefaultListableBeanFactory (org.springframework.beans.factory.support)
finishBeanFactoryInitialization:1024, AbstractApplicationContext (org.springframework.context.support)
refresh:614, AbstractApplicationContext (org.springframework.context.support)
<init>:150, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:98, ClassPathXmlApplicationContext (org.springframework.context.support)
<init>:15, MyClassPathXmlApplicationContext (com.mashibing)
main:84, Test (com.mashibing)
在这里执行setAsText方法,跳转到自定义实现的编辑器
开始解去填充转换为Address对象
执行结束:效果如下