了解Spring依赖注入(也就是IOC或者DI)过程的朋友都应该知道。在Spring依赖注入中有两个非常关键的接口,那就是BeanDefinition和BeanWrapper。BeanDefinition是定义在Spring配置文件中元素解析后的对象。而今天我们就来分析一下BeanWrapper。如果大家不知道Spring IOC的话,先来给大家讲一讲Spring IOC的主要过程。Spring依赖注入是以BeanFactory接口中的getBean()方法触发的。下面简单画一下依赖注入的时序图:
其实Spring IOC的整个过程都包括在AbstractAutowireCapableBeanFactory#doCreateBean方法中,而这个方法包括3个小方法。
上面这三个方法中BeanWrapper和BeanDefinition始终贯彻依赖注入的整个过程。由BeanDefinition提供数据,然后BeanWrapper负责依赖注入。具体是由BeanWrapper的实现类BeanWrapperImpl来完成的。下面我们看一下BeanWrapperImpl的类结构图。
下面我们来分析一下图中几个接口干的事:
注册JavaBean的PropertyEditor的概括方法,对PropertyEditorRegistrar起作用的中心接口。
public interface PropertyEditorRegistry {
void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor);
void registerCustomEditor(Class requiredType, String propertyPath, PropertyEditor propertyEditor);
PropertyEditor findCustomEditor(Class requiredType, String propertyPath);
}
这个接口就三个方法,根据方法我们可以知道。这个是对自定义PropertyEditor的注册与发现。而PropertyEditor是Java内省里面的接口,用于改变指定property属性的类型。可以结合下面的TypeConverter接口一起思考。
定义类型转换方法的接口,典型的(但是不是必须的)是和PropertyEditorRegistry接口一起实现。
public interface TypeConverter {
<T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException;
<T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
throws TypeMismatchException;
<T> T convertIfNecessary(Object value, Class<T> requiredType, Field field)
throws TypeMismatchException;
}
这三个方法上面都有共同的注释:
/**
* Convert the value to the required type (if necessary from a String).
* Conversions from String to any type will typically use the {@code setAsText}
* method of the PropertyEditor class, or a Spring Converter in a ConversionService.
/
它的意思是转换定义在xml中的值为指定类型,使用PropertyEditor类中的setAsText()方法转换String类型到任何类型。
典型的应用:
我们如果目标对象是Resource可以直接在xml定义这个Resource为String类型。因为Spring有相应的PropertyEditor实现类ResourceEditor。
例如我们定义Mybatis的配置文件:
id="userSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations">
<list>
classpath:com.spring.framework.carl.user.mapper/*.xml
list>
property>
但是我们可以看看SqlSessionFactoryBean:
我们可以看到在SqlSessionFactoryBean中mapperLocations的属性是Resource.但是我们在配置文件中并没有指定它的类型。这个活就是TypeConverter干的。
类可以访问指定properties的通用接口(例如:一个对象里面的bean properties或者一个对象的fields).做为BeanWrapper的基础接口。
public interface PropertyAccessor {
/**
* Path separator for nested properties.
* Follows normal Java conventions: getFoo().getBar() would be "foo.bar".
*/
String NESTED_PROPERTY_SEPARATOR = ".";
char NESTED_PROPERTY_SEPARATOR_CHAR = '.';
/**
* Marker that indicates the start of a property key for an
* indexed or mapped property like "person.addresses[0]".
*/
String PROPERTY_KEY_PREFIX = "[";
char PROPERTY_KEY_PREFIX_CHAR = '[';
/**
* Marker that indicates the end of a property key for an
* indexed or mapped property like "person.addresses[0]".
*/
String PROPERTY_KEY_SUFFIX = "]";
char PROPERTY_KEY_SUFFIX_CHAR = ']';
boolean isReadableProperty(String propertyName);
boolean isWritableProperty(String propertyName);
Class getPropertyType(String propertyName) throws BeansException;
TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
Object getPropertyValue(String propertyName) throws BeansException;
void setPropertyValue(String propertyName, Object value) throws BeansException;
void setPropertyValue(PropertyValue pv) throws BeansException;
void setPropertyValues(Map map) throws BeansException;
void setPropertyValues(PropertyValues pvs) throws BeansException;
void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
throws BeansException;
void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException;
}
由上面我们可以浓缩为4个方法:
1. isReadableProperty().判断指定property是否可读,是否包含getter方法。
2. isWritableProperty().判断指定property是否可写,是否包含setter方法。
3. getPropertyType().获取指定propertyName的类型。
4. setPropertyValue().设置指定propertyValue.
这4个方法是依赖注入的核心方法。
将PropertyAccessor的配置方法封进内部的接口.并且继承了PropertyEditorRegistry接口,定义了PropertyEditor的管理方法,主要是为BeanWrapper服务。
public interface ConfigurablePropertyAccessor extends PropertyAccessor,
PropertyEditorRegistry, TypeConverter {
void setConversionService(ConversionService conversionService);
ConversionService getConversionService();
void setExtractOldValueForEditor(boolean extractOldValueForEditor);
boolean isExtractOldValueForEditor();
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
boolean isAutoGrowNestedPaths();
}
其中主要的是以下2个方法:
1. void setConversionService(ConversionService conversionService);
2. ConversionService getConversionService();
用于集成Spring中的ConversionService.
public interface BeanWrapper extends ConfigurablePropertyAccessor {
void setAutoGrowCollectionLimit(int autoGrowCollectionLimit);
int getAutoGrowCollectionLimit();
Object getWrappedInstance();
Class getWrappedClass();
PropertyDescriptor[] getPropertyDescriptors();
PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
}
这个对象有4个方法比较重要:
1. getWrappedInstance().获取包装对象的实例。
2. getWrappedClass().获取包装对象的类型。
3. getPropertyDescriptors().获取包装对象所有属性的PropertyDescriptor.就是这个属性的上下文。
4. getPropertyDescriptor().获取包装对象指定属性的上下文。
BeanWrapper接口的默认实现类。用于对Bean的包装,实现上面接口所定义的功能很简单包括设置获取被包装的对象,获取被包装bean的属性描述器。
注意:默认情况下它会自动注册org.springframework.beans.propertyeditors包下面的property editors.是JDK标准的PropertyEditors的扩展。我们也可以调用registerCustomEditor()方法来给特别的实例注册一个编辑器。(i.e. they are not shared across the application)。可以查看PropertyEditorRegistrySupport了解更多详情。