前言
在平时的开发中,对于Spring为我们提供的数据类型转换与校验功能似乎已经使用的习以为常,但是对于其是如何在Spring框架背后运行的以及它自身的体系结构,一直以来我都没有一个特别清晰的认识,因此花了几天时间将Spring文档再次精读了一遍,同时对部分源码做了分析,故在此将自己的学习成果进行一个总结。原本想一篇文章搞定的,发现如果想细致分析根本一篇文章搞不定,所以我在这里将其拆分成3文章,本系列文章所分析的内容主要包括以下几个方面:
- PropertyEditor类型转换体系以及源码分析其背后的工作流程
- Type Conversion、Formatter SPI转换体系的结构以及核心类的源码分析
- Spring的数据校验的工作机制以及源码分析校验的执行流程
本篇文章首先对ProeprtyEditor
转换体系以及源码做一个分析,如果分析有不对之处,还望指正错误!!!
Spring类型转换体系
Spring的类型转换系统在经历了版本升级变更之后,也随之发生了很大的变化,从3.0前的PropertyEditor体系转变到了3.0后的Converter和Formatter转换体系。这里会先从早期的PropertyEditor开始分析,然后再分析3.0后的类型转换体系;分析ProperyEditor更有助于我们对Spring内部实现机制的理解,然后通过不同的类型转换系统进行一个对比,理解版本升级所带来的意义与好处。
1. PropertyEditor类型转换体系分析
首先让我们对传统的PropertyEditor
类型转换体系做一个复习与分析,来看看在一个看似不起眼的数据转换操作在其背后到底发生了什么。
1.1 PropertyEditor
通过java.beans.PropertyEditor
其包名我们就可以看出,其本身并非Spring中定义的接口,它其实是Java Bean
规范中所定义的一个接口,其设计初衷在于是用于完成GUI中的输入与对象属性之间的转换操作;Spring只是借用了PropertyEditor
这个接口的定义与功能,来完成字符串与对象属性之间的转换。在Spring 3.0之前,在Spring整个体系中,所有完成字符串与其他数据类型转换操作都是由ProperyEditor
接口来完成的。
Spring通过PropertyEditor
作为其数据类型转换的基础架构,同时自己还定义了许多默认的ProrpertyEditor
的实现,这些实现类都位于spring-beans
这个模块中的propertyeditors
包中。
![](https://img.haomeiwen.com/i1684370/ee520f8e7f7e9e9a.jpg)
这些内置的PropertyEditor
会有部分在默认情况下就已经被加了到IOC容器中,而有些PropertyEditor
在默认情况下并没有自动加入,需要用户手动进行配置,后面我们通过源码可以看到Spring默认所注册了哪些PropertyEditor
。
1.2 PropertyEditorSupport
由于PropertyEditor
是一个类型转换的接口,其里面定义了很多与我们实际使用上无关的方法。如果我们想要使用PropertyEditor
的话,我们通常只需要继承java.beans.PropertyEditorSupport
这个类,并且重写其setAsText(String source)
方法即可,通过它将输入的字符串转换成我们期望的数据类型。
![](https://img.haomeiwen.com/i1684370/b2ff5e76b1dfa1b5.jpg)
通过上图可以看到,Spring中的所提供的内置的
PropertyEditor
也都是继承PropertyEditorSupport
来完成类型转换的。
1.3 PropertyEditor的基本使用
下面我们通过一个简单的例子来复习一下PropertyEditor
的基本使用。这里为了减少篇幅,我就不给出需要的maven依赖,之后如果需要参考的话可以直接到GitHub下载源码。
package com.panlingxiao.spring.validation.domain;
import lombok.Data;
/**
* Created by panlingxiao on 2016/5/29.
* 这里使用lombok来取代getter和setter方法。
*/
@Data
public class Circle {
private Point point;
}
package com.panlingxiao.spring.validation.domain;
import lombok.Data;
@Data
public class Point {
int x, y;
}
上面给出两个非常简单的类,我们希望完成的是输入1;2
,能够自动进行分割,然后转换成point
的x和y属性。
下面我们自己定义PropertyEditor来完成数据的转换:
package com.panlingxiao.spring.validation.editor;
import com.panlingxiao.spring.validation.domain.Point;
import java.beans.PropertyEditorSupport;
/**
* 自定义PropertyEditor,完成String到Point的转换
*/
public class PointEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
String[] splits = text.split(";");
Point point = new Point();
point.setX(Integer.parseInt(splits[0]));
point.setY(Integer.parseInt(splits[1]));
/*
*需要将装换后的结果设置到Editor的value属性中,因为外部会通过getValue获取到转换的结果。
*/
setValue(point);
}
}
在完成ProperyEditor
编写完成后,我们只需将其注册到IOC容器中就可以自动完成String
到Point
之间的转换,下面来编写spring的配置文件:
<?xml version="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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 通过CustomEditorConfigurer这个BeanFactoryProcessor来完成自定义的ProperyEditor到IOC容器的添加功能 -->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<bean class="com.panlingxiao.spring.validation.editor.MyPropertyEditorRegistrar"/>
</property>
</bean>
<bean class="com.panlingxiao.spring.validation.domain.Circle" id="circle">
<property name="point" value="1;2"/>
</bean>
</beans>
在编写完成配置文件之后,我们编写一个测试类来验证一下配置的结果是否有效:
package com.panlingxiao.spring.propertyeditor;
import com.panlingxiao.spring.validation.domain.Circle;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by panlingxiao on 2016/6/1.
*/
public class TestPropertyEditor {
@Test
public void testCustomEditorConfigurer() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("prop-editor-app-context.xml");
Circle circle = applicationContext.getBean("circle", Circle.class);
System.out.println(circle.getPoint());
}
}
![](https://img.haomeiwen.com/i1684370/3a5295c435eea249.jpg)
除了通过CustomEditorConfigurer
来完成自定义PropertyEditor
的注入,Spring也支持另外一种基于Java Bean
规范的自动查找机制。下面我们也通过一个例子来验证一下这种方式:
package com.panlingxiao.spring.validation.domain;
import lombok.Data;
/**
* Created by panlingxiao on 2016/5/29.
*/
@Data
public class Boo {
private Foo foo;
}
package com.panlingxiao.spring.validation.domain;
import lombok.Data;
/**
* Created by panlingxiao on 2016/5/29.
*/
@Data
public class Foo {
private int x,y;
}
上面给出两个类也同样非常简单,我们也希望完成的是输入1;2,能够自动进行分割,然后转换成一个Foo对象的x和y属性即可,下面我们再看定义自己的PropertyEditor
:
package com.panlingxiao.spring.validation.domain;
import java.beans.PropertyEditorSupport;
/**
* Created by panlingxiao on 2016/5/29.
*/
public class FooEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
String[] splits = text.split(";");
Foo foo = new Foo();
foo.setX(Integer.parseInt(splits[0]));
foo.setY(Integer.parseInt(splits[1]));
setValue(foo);
}
}
注意到:自定义的PropertyEditor必须与对应的类型在同一个包下,并且其名字必须为xxxEditor
,这样才能实现自动查询的功能
![](https://img.haomeiwen.com/i1684370/c54e5199eb6972c1.png)
下面,我们通过一个测试类来简单的验证一下该功能是否真的有效,首先编写一个XML配置文件:
<?xml version="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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.panlingxiao.spring.validation.domain.Boo" id="boo">
<property name="foo" value="1;2"/>
</bean>
</beans>
/**
* 通过约定机制来自动查询PropertyEditor完成类型转换
*/
@Test
public void testPropertyEditorByConvention(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("prop-convention-app-context.xml");
Boo boo = ctx.getBean("boo", Boo.class);
System.out.println("foo.x: "+boo.getFoo().getX()+",foo.y: "+boo.getFoo().getY());
}
![](https://img.haomeiwen.com/i1684370/aba3c37876dc2f9c.png)
通过上面的运行结果我们可以看到,我们自己所定义的ProeprtyEditor
确实已经被Spring的IOC容器所管理,并且成功地完成了类型的转换操作。但是Spring容器在背后具体做了什么,对于我们而言却完全一无所知,下面我们将继续深入学习一下,并且从源码的角度仔细分析一下Spring如何对PropertyEditor
进行管理的,如何将字符串转换成我们期望的类型。
1.4 深入分析PropertyEditor类型转换体系以及源码分析
PropertyEditor类型转换体系牵涉到的类比较多,下面我们对其涉及到接口与类逐一进行分析,在了解了核心接口与类之后,再去看Spring中的PropertyEditor类型转换体系就会清晰很多。
首先,我们应该所思考的问题是:Spring中的内置的PropertEditor
与我们定义的ProeprtyEditor
是被谁来完成管理的呢?是BeanFactory
还是ApplicationContext
呢?这样的问题都不算非常准确,真正完成管理功能应该是PropertyEditorRegistry
,下面我们对这个接口做一个比较细致的分析。
PropertyEditorRegistry
org.springframework.beans.PropertyEditorRegistry
接口提供了对PropertyEditor
注册以及查找功能,因此其主要提供是提供了对PropertyEditor
的管理功能,首先来看看这个接口的描述:
![](https://img.haomeiwen.com/i1684370/cd47b1c5abdf0565.png)
下面是该接口中所定义的方法:
/**
根据属性的类型来指定其对应的PropertyEditor
*/
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
/**
* 根据属性的类型以及属性的名字来指定其对应的PropertyEditor,该方法并
* 不常用,但是可以做到更细粒度的转换操作
*/
void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor);
/**
根据指定的类型以及属性的名字,查询其对应的ProeprtyEditor,属性的名字可以为null
*/
PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath);
通过上面的描述,我们应该可以明白PropertyEditorRegistry
这个接口的作用,下面我们来看看其具体的几个实现类。
PropertyEditorRegistrySupport
由于PropertyEditorRegistry
只是定义对PropertyEditor
注册和查找的方法,其具体的核心实现类是org.springframework.beans.PropertyEditorRegistrySupport
,真正对PropertyEditor
管理的操作全部在该类中实现,下面来看看PropertyEditorRegistrySupport
的源码,由于PropertyEditorRegistrySupport
源码篇幅比较多,这里就采用截图来说明其实现:
![](https://img.haomeiwen.com/i1684370/e16b9ee86f14386c.jpg)
通过上面的标注我们看到PropertyEditorSupport
底层对于不同种类的PropertyEditor
使用不同的Map来进行存储,下面我们看下它是如何进行注册的。
![](https://img.haomeiwen.com/i1684370/4cbae78c3a610fea.jpg)
其注册的实现机制也并没有出人意料的地方,就是判断存储Classs
与PropertyEditor
之间映射关系的Map是否已经存在,如果不存在则先创建一个LinkedHashMap
,如果有就直接进行存储映射关系。
前面我们提到过在IOC容器中默认就会内置一些PropertyEditor
,通过createDefaultEditors()
我们可以清楚地看到其默认所添加的PropertyEditor
。
![](https://img.haomeiwen.com/i1684370/0e34e4e0ae3a3d9f.jpg)
上面就是PropertyEditorRegistrySupport
最核心的实现,下面继续来看另外一个重要的接口。
PropertyEditorRegistrar
初看PropertyEditorRegistrar
时,可能会因为与PropertyEditorRegistry
在名字上的相似性而混淆,下面我们看下PropertyEditorRegistrar
到底是干嘛的。
![](https://img.haomeiwen.com/i1684370/6ecb75567d4c0c9c.png)
从接口的描述上我们可以看到,PropertyEditorRegistrar
的作用是将用户自定义的PropertyEditor
注册到PropertyEditorRegistry
中。通过其registerCustomEditor
方法中的参数我们可以看到,其所接受的正是一个PropertyEditorRegistry
,通过方法的参数将用户自定义的ProepertyEditor
加入到PropertyEditorRegistry
被其进行管理。
PropertyEditorRegistrar
对于如果我们希望将一组相同的PropertyEditor
应用在多个地方时是非常有用的 ( 比如希望将相同的一组PropertyEditor
既应用在IOC容器中,同时又应用在Spring MVC的DataBinder
中),此时就可以先定义一个PropertyEditorRegistrar
的实现类,来完成通用的ProepertyEditor
注册操作,然后将PropertyEditorRegistrar
作为一个ProeprtyEditor
的集合设置到不同的地方,此时就可以做到代码复用。
PS:相同的PropertyEditor需要在多处进行注册的原因是因为我们在IOC容器中通过CustomEditorConfigurer添加了自定义的PropertyEditor后,其并不会对SpringMVC中所使用的DataBinder而生效,因此需要再次进行注册,我们通过分析CustomEditorConfigurer可以在其注释说明中清楚地看到这点说明。
![](https://img.haomeiwen.com/i1684370/021f0789292e5746.png)
下面我们来看一个PropertyEditorRegistrar
的例子,我们还是使用上面的例子作为基础,首先定义一个PropertyEditorRegistrar
来完成PropertyEditor
的注册功能:
package com.panlingxiao.spring.validation.editor;
import com.panlingxiao.spring.validation.domain.Point;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
/**
* Created by panlingxiao on 2016/6/2.
*/
public class MyPropertyEditorRegistrar implements PropertyEditorRegistrar {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
//将自己所定义的PropertyEditor注册到PropertyEditorRegistry中
registry.registerCustomEditor(Point.class,"point",new PointEditor());
}
}
我们只需修改一下配置文件,现在要注入的不再是一个Map,而是一个PropertyEditorRegistrar
。
<?xml version="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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
通过CustomEditorConfigurer这个BeanFactoryProcessor来完成自定义的ProperyEditor到IOC容器的添加功能
-->
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<bean class="com.panlingxiao.spring.validation.editor.MyPropertyEditorRegistrar"/>
</property>
<bean class="com.panlingxiao.spring.validation.domain.Circle" id="circle">
<property name="point" value="1;2"/>
</bean>
</beans>
/**
* 测试基于{@link org.springframework.beans.PropertyEditorRegistrar}的PropertyEditor的注册
*/
@Test
public void testPropertyEditorByPropertyRegistrar(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("prop-registrar-app-context.xml");
Circle circle = ctx.getBean("circle", Circle.class);
Assert.assertEquals(1,circle.getPoint().getX());
Assert.assertEquals(2,circle.getPoint().getY());
}
运行结果:
![](https://img.haomeiwen.com/i1684370/99375f79b60f3384.png)
通过上面的结果我们看到了PropertyRegistrar
的作用以及基本用法,下面我们再介绍一个比较底层接口,由于该接口和源码息息相关,因此不得不在此做出说明,它就是BeanWrapper
接口:
BeanWrapper
BeanWrapper是Spring中一个比较底层的接口,在通常情况下,我们作为普通用户是不会涉及到这个接口的使用的,其主要被Spring的内部所使用,但是由于我们下面会设计到IOC部分的源码分析,故在此也对其做一个说明,对于查看后面的源码会更有帮助。
![](https://img.haomeiwen.com/i1684370/7b5f741ebc9225a4.png)
从上面的描述我们可以了解到BeanWrapper
接口的基本作用。BeanWrapper
除了提供了标准的JavaBean的访问方式以外,同时还继承了PropertyEditorRegistry
,因此BeanWrapper
也同样具有对ProeprtyEditor
的管理功能,下面是BeanWrapper
的继承结构图:
![](https://img.haomeiwen.com/i1684370/4ecf47a739be9bea.png)
Spring底层其实在创建一个Bean时,IOC容器首先将用户自定义的PropertyEditor
注册到BeanWrapper
中,然后通过BeanWrapper
通过注入的PropertyEditor
完成数据类型的转换,再将转换后的结果通过反射注入到Bean的属性中,该结论可以在调式后续的源码中得以验证。
下面我们先通过一个简单的例子来简单说明一下BeanWrapper
的使用:
package com.panlingxiao.spring.validation.domain;
import com.panlingxiao.spring.validation.annotation.MyDate;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.Id;
import java.util.Date;
import java.util.List;
/**
* Created by panlingxiao on 2016/5/26.
*/
@Data
public class Person {
@Id
private Integer id;
private String name;
private int age;
private Date birthday;
private List<Integer> nums;
private Boo boo;
private Point point;
}
/**
* 使用BeanWrapperImpl设置基本属性
*/
@Test
public void testSetBasicPropertyValue(){
Person person = new Person();
BeanWrapperImpl wrapper = new BeanWrapperImpl(person);
wrapper.setPropertyValue("id", "1");
Assert.assertEquals(1, person.getId().intValue());
//设置BeanWrapper自动创建内联属性,否则会引发NullValueInNestedPathException
wrapper.setAutoGrowNestedPaths(true);
wrapper.setPropertyValue("nums[0]", "123");
wrapper.setPropertyValue("nums[1]", "456");
System.out.println(person.getNums());
wrapper.setPropertyValue("boo.foo.x", "1");
System.out.println(person.getBoo().getFoo().getX());
//注册自定义的PropertyEditor到BeanWrapper中
wrapper.registerCustomEditor(Point.class,new PointEditor());
//BeanWrapper通过注册的PropertyEditor完成数据类型的转换
wrapper.setPropertyValue("point","1;2");
}
运行结果:
![](https://img.haomeiwen.com/i1684370/fa49af676dfc1b25.png)
至此,对于PropertyEditor
体系中牵涉到几个重要概念就介绍完了,下面我们需要具体来看看我们自己定义的PropertyEditor
是如何被添加到IOC容器中,又是如何被IOC添加到BeanWrapper
中的,最终BeanWrapper
又是如何完成数据类型的转换的。
1.首先我们看看CustomEditorConfigurer
是如何自动被IOC容器所加载然后调用的。
![](https://img.haomeiwen.com/i1684370/c57e79d71e7b820c.png)
我们可以看到CustomEditorConfigurer
实现了BeanFactoryPostProcessor
接口,BeanFactoryPostProcessor
是Spring提供给用户用于动态修改application context信息的接口,对于application context它会自动去查询所有的BeanFactoryPostProcessor
,并且调用其postProcessBeanFactory
方法来完成处理,需要注意的是,该方法的调用是在所有单例Bean初始化前调用的。
其具体代码体现为:
![](https://img.haomeiwen.com/i1684370/f795de12362e4137.png)
通过上面的代码可以看到,AbstractApplicationContext
的refresh()
方法会自动完成BeanFactoryPostProcessor
的查找和调用,下面我们具体看看它的具体处理流程,由于invokeBeanFactoryPostProcessors
中的方法代码过多,这里我只截取核心的处理代码:
![](https://img.haomeiwen.com/i1684370/929bf2b9f25054af.png)
![](https://img.haomeiwen.com/i1684370/6e7d2ce48d8b4e76.png)
![](https://img.haomeiwen.com/i1684370/3705d5149ae3cc06.png)
可以看到,它会自动去BeanFactory
根据类型获取到BeanFactoryPostProcessor
的名字,然后根据名字查询到具体的实例,最后调用其定义postProcessBeanFactory
完成用户自己的处理。
通过上面的分析,我们应该能够清楚地看到一个BeanFactoryPostProcesor
是如何被ApplicationContext
所处理的,而我们所配置的CustomEditorConfigurer
正是一个BeanFactoryPostProcesor
。
2.下面我们来看看CustomEditorConfigurer
的postProcessBeanFactory
具体实现,分析我们所定义的PropertyEditor
是如何被IOC容器所使用:
@SuppressWarnings("unchecked")
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
/*
* 将用户所定义的PropertyEditorRegistrar注入到BeanFactory中,
* BeanFactory底层会通过BeanWrapper来创建Bean,此时BeanFactory就会将其所管理的PropertyEditor传递给
* BeanWrapper底层又维护着一个TypeConverterDelegate,通过TypeConverterDelegate来真正完成真正的类型转换
* 再将转换后结果通过BeanWrapper使用反射注入到属性中。
*/
if (this.propertyEditorRegistrars != null) {
for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) {
beanFactory.addPropertyEditorRegistrar(propertyEditorRegistrar);
}
}
//迭代存储Class与PropertyEditor映射关系的map
if (this.customEditors != null) {
for (Map.Entry<String, ?> entry : this.customEditors.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Class requiredType = null;
try {
//通过反射获取具体要转换的Java类型
requiredType = ClassUtils.forName(key, this.beanClassLoader);
//判断value的具体类型,可能是一个PropertyEditor、也可能是一个Class、也可能是一个String
//如果设置的是一个具体的PropertyEditor,那么该被PropertyEditor会被共享使用,每次使用都会被加锁来处理,因此建议不要
//将PropertyEditor设置为共享,从而影响性能
if (value instanceof PropertyEditor) {
if (logger.isWarnEnabled()) {
logger.warn("Passing PropertyEditor instances into CustomEditorConfigurer is deprecated: " +
"use PropertyEditorRegistrars or PropertyEditor class names instead. " +
"Offending key [" + key + "; offending editor instance: " + value);
}
beanFactory.addPropertyEditorRegistrar(
new SharedPropertyEditorRegistrar(requiredType, (PropertyEditor) value));
}
//当设置的是一个Class,则直接存储转换类型与PropertyEditor的类型
else if (value instanceof Class) {
beanFactory.registerCustomEditor(requiredType, (Class) value);
}
//当设置的是一个String,则先反射出PropertyEditor,再存储转换类型与PropertyEditor的类型
else if (value instanceof String) {
Class editorClass = ClassUtils.forName((String) value, this.beanClassLoader);
Assert.isAssignable(PropertyEditor.class, editorClass);
beanFactory.registerCustomEditor(requiredType, editorClass);
}
else {
throw new IllegalArgumentException("Mapped value [" + value + "] for custom editor key [" +
key + "] is not of required type [" + PropertyEditor.class.getName() +
"] or a corresponding Class or String value indicating a PropertyEditor implementation");
}
}
catch (ClassNotFoundException ex) {
if (this.ignoreUnresolvableEditors) {
logger.info("Skipping editor [" + value + "] for required type [" + key + "]: " +
(requiredType != null ? "editor" : "required type") + " class not found.");
}
else {
throw new FatalBeanException(
(requiredType != null ? "Editor" : "Required type") + " class not found", ex);
}
}
}
}
}
3.看完自定义的PropertyEditor
如何被加入到BeanFactory
中之后,下面我们继续看BeanFactory
是在何时创建一个我们所定义的Bean,并且在创建Bean后如何通过BeanWrapper
将PropertyEditor
完成类型转换。
![](https://img.haomeiwen.com/i1684370/3480298cf54ad2f2.png)
![](https://img.haomeiwen.com/i1684370/332d96009fd8dcdb.png)
这里为了减少篇幅,我直接给出代码的截图,通过上面的的方法名我们可以看到,ApplicationContext
会预先完成单例的Bean的初始化操作,下面我们看看它是如何预初始化单例的Bean的,由于这里的代码比较深,我们直接跳到我们所最关注的部分,即IOC如何使用BeanWrapper部分。
![](https://img.haomeiwen.com/i1684370/3b35ad959e8a7fe4.png)
通过上图我们看到了一个initBeanWrapper方法,该方法就是IOC容器将用户所配置的PropertyEditor注册到BeanWrapper的过程,下面我们继续查看该方法具体做了什么。
![](https://img.haomeiwen.com/i1684370/c8ffa12e4e688194.png)
![](https://img.haomeiwen.com/i1684370/5639ff8d2665b8bc.png)
![](https://img.haomeiwen.com/i1684370/ee13a50bcae8c2e9.png)
看到BeanFactory将PropertyEditor往BeanWrapper注册之后,下面就应该完成Bean的属性的注入操作。
![](https://img.haomeiwen.com/i1684370/74620fd541686529.png)
populate方法是完成Bean的属性注入操作,通过上面的截图看到,在未调用该方法之前,此时对象的属性还是null,下面是其如何完成数据类型的转换过程:
![](https://img.haomeiwen.com/i1684370/83e2de96e3eb30db.png)
![](https://img.haomeiwen.com/i1684370/2f6f5ab13200509e.png)
在通过TypeConverterDelegate完成属性类型转换后,最后再通过BeanWrapper将属性注入到对象中。
![](https://img.haomeiwen.com/i1684370/83958b891a2cfa8d.png)
至此,PropertyEditor
的体系结构以及其如何在IOC容器中的过程已经分析完了。这里需要说明的一下,由于Spring的源码过多,我曾尝试想在博客中进行逐行分析,但是发现根本不可能,所以上面的分析也只能以截图的形式给出,对于具体的细节如果想更进一步深入,就需要自己再进一步看源码和调式。说了这么多关于PropertyEditor
的事,我们最后所要思考的是PropertyEditor
其所存在的缺点。
PropertyEditor的缺点分析
1.只能完成字符串到Java类型的转换,并不能完成任意类型之间的转换。
2.由于PropertyEditor是非线程安全,因此对于每一次的类型转换,都需要创建一个新的PropertyEdtitor,如果希望达到共享,那么底层会使用synchronized来对其进行并发地控制。
![](https://img.haomeiwen.com/i1684370/c74538dee51c25b2.png)
由于PropertyEditor
存在着这些问题,因此Spring3.0后推出了新的类型转换系统,下一节将继续对Conversion Service
的体系结构以及部分源码进行分析。
PS:用心写作真心不易,如果您觉得本文对您有帮助,请点个赞,这是对我最大的鼓励,谢谢!
网友评论