深入分析Spring中的类型转换与校验(1)

前言

在平时的开发中,对于Spring为我们提供的数据类型转换校验功能似乎已经使用的习以为常,但是对于其是如何在Spring框架背后运行的以及它自身的体系结构,一直以来我都没有一个特别清晰的认识,因此花了几天时间将Spring文档再次精读了一遍,同时对部分源码做了分析,故在此将自己的学习成果进行一个总结。原本想一篇文章搞定的,发现如果想细致分析根本一篇文章搞不定,所以我在这里将其拆分成3文章,本系列文章所分析的内容主要包括以下几个方面:

  • PropertyEditor类型转换体系以及源码分析其背后的工作流程
  • Type Conversion、Formatter SPI转换体系的结构以及核心类的源码分析
  • Spring的数据校验的工作机制以及源码分析校验的执行流程

本篇文章首先对ProeprtyEditor转换体系以及源码做一个分析,如果分析有不对之处,还望指正错误!!!

Spring类型转换体系

Spring的类型转换系统在经历了版本升级变更之后,也随之发生了很大的变化,从3.0前的PropertyEditor体系转变到了3.0后的ConverterFormatter转换体系。这里会先从早期的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包中。

Spring内置的PropertyEditor的实现.jpg

这些内置的PropertyEditor会有部分在默认情况下就已经被加了到IOC容器中,而有些PropertyEditor在默认情况下并没有自动加入,需要用户手动进行配置,后面我们通过源码可以看到Spring默认所注册了哪些PropertyEditor

1.2 PropertyEditorSupport

由于PropertyEditor是一个类型转换的接口,其里面定义了很多与我们实际使用上无关的方法。如果我们想要使用PropertyEditor的话,我们通常只需要继承java.beans.PropertyEditorSupport这个类,并且重写其setAsText(String source)方法即可,通过它将输入的字符串转换成我们期望的数据类型。

Spring中的PorperyEdidtor.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容器中就可以自动完成StringPoint之间的转换,下面来编写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());
    }
}
PropertyEditor测试运行结果.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,这样才能实现自动查询的功能

PropertyEditor自动查询机制.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());
    }
PropertyEditor自动查询机制运行结果.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的管理功能,首先来看看这个接口的描述:

PropertyEditorRegistry接口描述.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源码篇幅比较多,这里就采用截图来说明其实现:

PropertyEditorSupport分析.jpg

通过上面的标注我们看到PropertyEditorSupport底层对于不同种类的PropertyEditor使用不同的Map来进行存储,下面我们看下它是如何进行注册的。

PropertyEditorSupport的注册过程.jpg

其注册的实现机制也并没有出人意料的地方,就是判断存储ClasssPropertyEditor之间映射关系的Map是否已经存在,如果不存在则先创建一个LinkedHashMap,如果有就直接进行存储映射关系。
前面我们提到过在IOC容器中默认就会内置一些PropertyEditor,通过createDefaultEditors()我们可以清楚地看到其默认所添加的PropertyEditor

IOC中默认所内置的PropertyEditor.jpg

上面就是PropertyEditorRegistrySupport最核心的实现,下面继续来看另外一个重要的接口。

PropertyEditorRegistrar

初看PropertyEditorRegistrar时,可能会因为与PropertyEditorRegistry在名字上的相似性而混淆,下面我们看下PropertyEditorRegistrar到底是干嘛的。

PropertyRegistrar的接口说明.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可以在其注释说明中清楚地看到这点说明。

使用PropertyRegistartr进行配置的原因.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());
    }

运行结果:

PropertyEditorRegistart的注册运行结果.png

通过上面的结果我们看到了PropertyRegistrar的作用以及基本用法,下面我们再介绍一个比较底层接口,由于该接口和源码息息相关,因此不得不在此做出说明,它就是BeanWrapper接口:

BeanWrapper

BeanWrapper是Spring中一个比较底层的接口,在通常情况下,我们作为普通用户是不会涉及到这个接口的使用的,其主要被Spring的内部所使用,但是由于我们下面会设计到IOC部分的源码分析,故在此也对其做一个说明,对于查看后面的源码会更有帮助。


BeanWrapper接口说明.png

从上面的描述我们可以了解到BeanWrapper接口的基本作用。BeanWrapper除了提供了标准的JavaBean的访问方式以外,同时还继承了PropertyEditorRegistry,因此BeanWrapper也同样具有对ProeprtyEditor的管理功能,下面是BeanWrapper的继承结构图:

BeanWrapper继承结构图.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");        
    }

运行结果:

BeanWrapper测试运行结果.png

至此,对于PropertyEditor体系中牵涉到几个重要概念就介绍完了,下面我们需要具体来看看我们自己定义的PropertyEditor是如何被添加到IOC容器中,又是如何被IOC添加到BeanWrapper中的,最终BeanWrapper又是如何完成数据类型的转换的。

1.首先我们看看CustomEditorConfigurer是如何自动被IOC容器所加载然后调用的。

CustomEditorConfigurer实现了BeanFactoryProcessor.png

我们可以看到CustomEditorConfigurer实现了BeanFactoryPostProcessor接口,BeanFactoryPostProcessor是Spring提供给用户用于动态修改application context信息的接口,对于application context它会自动去查询所有的BeanFactoryPostProcessor,并且调用其postProcessBeanFactory方法来完成处理,需要注意的是,该方法的调用是在所有单例Bean初始化前调用的。

其具体代码体现为:


ApplicationContext完成BeanFactoryPostProcessor初始化过程.png

通过上面的代码可以看到,AbstractApplicationContextrefresh()方法会自动完成BeanFactoryPostProcessor的查找和调用,下面我们具体看看它的具体处理流程,由于invokeBeanFactoryPostProcessors中的方法代码过多,这里我只截取核心的处理代码:

ApplicationContext自动查找BeanFactoryPostProcessor.png
ApplicationContext自动查找BeanFactoryPostProcessor2.png
BeanFactoryPostProcessor在BeanFactory中的执行.png

可以看到,它会自动去BeanFactory根据类型获取到BeanFactoryPostProcessor的名字,然后根据名字查询到具体的实例,最后调用其定义postProcessBeanFactory完成用户自己的处理。
通过上面的分析,我们应该能够清楚地看到一个BeanFactoryPostProcesor是如何被ApplicationContext所处理的,而我们所配置的CustomEditorConfigurer正是一个BeanFactoryPostProcesor

2.下面我们来看看CustomEditorConfigurerpostProcessBeanFactory具体实现,分析我们所定义的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后如何通过BeanWrapperPropertyEditor完成类型转换。

ApplicationContext完成SingleTonBean的创建.png
ApplicationContext完成SingleTonBean的创建2.png

这里为了减少篇幅,我直接给出代码的截图,通过上面的的方法名我们可以看到,ApplicationContext会预先完成单例的Bean的初始化操作,下面我们看看它是如何预初始化单例的Bean的,由于这里的代码比较深,我们直接跳到我们所最关注的部分,即IOC如何使用BeanWrapper部分。

Bean的初始化过程.png

通过上图我们看到了一个initBeanWrapper方法,该方法就是IOC容器将用户所配置的PropertyEditor注册到BeanWrapper的过程,下面我们继续查看该方法具体做了什么。


IOC注册PropertyEditor到BeanWrapper过程.png
IOC注册ProeprtyEditor的具体实现.png
IOC注册ProeprtyEditor的具体实现2.png

看到BeanFactory将PropertyEditor往BeanWrapper注册之后,下面就应该完成Bean的属性的注入操作。

属性注入的过程.png

populate方法是完成Bean的属性注入操作,通过上面的截图看到,在未调用该方法之前,此时对象的属性还是null,下面是其如何完成数据类型的转换过程:

类型转换的具体实现.png
类型转换过程2.png

在通过TypeConverterDelegate完成属性类型转换后,最后再通过BeanWrapper将属性注入到对象中。

属性注入代码.png

至此,PropertyEditor的体系结构以及其如何在IOC容器中的过程已经分析完了。这里需要说明的一下,由于Spring的源码过多,我曾尝试想在博客中进行逐行分析,但是发现根本不可能,所以上面的分析也只能以截图的形式给出,对于具体的细节如果想更进一步深入,就需要自己再进一步看源码和调式。说了这么多关于PropertyEditor的事,我们最后所要思考的是PropertyEditor其所存在的缺点。

PropertyEditor的缺点分析

1.只能完成字符串到Java类型的转换,并不能完成任意类型之间的转换。
2.由于PropertyEditor是非线程安全,因此对于每一次的类型转换,都需要创建一个新的PropertyEdtitor,如果希望达到共享,那么底层会使用synchronized来对其进行并发地控制。

PropertyEditor线程安全问题.png

由于PropertyEditor存在着这些问题,因此Spring3.0后推出了新的类型转换系统,下一节将继续对Conversion Service的体系结构以及部分源码进行分析。

PS:用心写作真心不易,如果您觉得本文对您有帮助,请点个赞,这是对我最大的鼓励,谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,490评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,678评论 6 342
  • 十里桃花 /青葵 三月。自你离开后的醒来 青袍玉箫,杳如黄鹤 我卸下浓妆,除去艳抹 折取青杯,捻不破一颗红豆 我不...
    石上青葵阅读 342评论 10 7
  • 写在开头,本人不是专业作家写手,格式之类的也不会特别重视,文章纯属个人情感抒发或是经验积累,如若给看官您造成任何负...
    吃货小耳猫阅读 331评论 0 0
  • 初始化项目 这里我们使用vue-cli来自动生成vue.js项目的模板。 安装Node.js 用npm安装vue-...
    danejahn阅读 968评论 2 52