spring之BeanWrapper分析

1:spring bean生命周期分析

这篇文章中我分析了spring bean的生命周期,为了方便本文的讲解,再来贴下生命周期的流程图:
在这里插入图片描述
我们看到图中有一步创建bean实例,对应的方法是createBeanInstance,调用位置源码如下:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

	// Instantiate the bean.
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		// <20210421 1702>
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	final Object bean = instanceWrapper.getWrappedInstance();
	Class<?> beanType = instanceWrapper.getWrappedClass();
	if (beanType != NullBean.class) {
		mbd.resolvedTargetType = beanType;
	}
	...snip...
}

<20210421 1702>处可以看出来createBenaInstance方法并不是bean本身,而是org.springframework.beans.BeanWrapper,在createBeanInstance方法中具体完成BeanWrapper创建的方法注意注意只是最终使用无参构造函数的情况,也是为了方便说明问题如下:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#instantiateBean
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
	try {
		Object beanInstance;
		final BeanFactory parent = this;
		if (System.getSecurityManager() != null) {
			beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
					getInstantiationStrategy().instantiate(mbd, beanName, parent),
					getAccessControlContext());
		}
		else {
			// 通过InstantiationStrategy实例化策略接口完成初始化
			beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
		}
		BeanWrapper bw = new BeanWrapperImpl(beanInstance);
		initBeanWrapper(bw);
		return bw;
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
	}
}

org.springframework.beans.BeanWrapper的唯一的实现类是org.springframework.beans.BeanWrapperImpl,其类图如下:
在这里插入图片描述
从图中可以看出,主要实现了如下的三个接口:

1:org.springframework.beans.PropertyEditorRegistry
2:org.springframework.beans.TypeConverter
3:org.springframework.beans.PropertyAccessor

接下来我们分别来分析下这3个接口,掌握了所实现接口提供的能力,才能更好的了解BeanWrapperImpl具有的能力。

2:PropertyAccessor

该接口的作用是存取属性值
本部分参考本文而成。
该接口全路径是org.springframework.beans.PropertyAccessor,作用是存/取bean的属性值,接口源码如下:

org.springframework.beans.PropertyAccessor
/*
定义访问bean的属性或者是字段的公共接口,BeanWrapper就实现了该接口
来获取bean属性或者字段访问的能力
*/
public interface PropertyAccessor {

	// 可以认为是属性级联的访问符,如foo.bar,相当于是getFoo().getBar(),通过字符串来定义
	String NESTED_PROPERTY_SEPARATOR = ".";
	// 可以认为是属性级联的访问符,如foo.bar,相当于是getFoo().getBar(),通过字符来定义
	char NESTED_PROPERTY_SEPARATOR_CHAR = '.';

	// 数组,List,Map等设置值的前后缀
	// 如有String[] arrStr = new String[1]; 想要设置第一个位置的值就是accessor.setPropertyValue("arrStr[0]", "arrStr");
	// 如有private Map<Integer, String> map = new HashMap<>();想要设置"1->张三"就是accessor.setPropertyValue("map[1]", "张三");
	String PROPERTY_KEY_PREFIX = "[";
	char PROPERTY_KEY_PREFIX_CHAR = '[';
	String PROPERTY_KEY_SUFFIX = "]";
	char PROPERTY_KEY_SUFFIX_CHAR = ']';


	// 读方法,获取属性类型,返回public final class Class<T> implements Type ...{}
	@Nullable
	Class<?> getPropertyType(String propertyName) throws BeansException;
	// 读方法,获取属性描述器,可参考:https://blog.csdn.net/wang0907/article/details/115287124
	@Nullable
	TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
	// 读方法,读取属性的值,有可能是数组,列表,map等嵌套数据结构
	@Nullable
	Object getPropertyValue(String propertyName) throws BeansException;


	// 写方法,设置属性值
	void setPropertyValue(String propertyName, @Nullable 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;

}

类继承层级结构如下图:
在这里插入图片描述
其中红色框中的是比较重要的子类。接下来我们使用DirectFieldAccessor来写个例子。

3.1:定义bean

/* !!! 注意所有的属性都没有提供读写方法 !!!*/
public class Apple {

    private String color;

    // 复杂类型
    private Size size = new Size();
    private String[] arrStr = new String[1];
    private List<String> listStr = new ArrayList<>();
    private Map<Integer, String> map = new HashMap<>();

    // 更为复杂的类型
    private List<List<String>> listList = new ArrayList<>();
    private List<Map<Integer, String>> listMap = new ArrayList<>();

    public Apple() {
        super();
        listList.add(new ArrayList<>());
        listMap.add(new HashMap<>());
    }

    @Override
    public String toString() {
        return "Apple{" +
                "color='" + color + '\'' +
                ", size=" + size +
                ", arrStr=" + Arrays.toString(arrStr) +
                ", listStr=" + listStr +
                ", map=" + map +
                ", listList=" + listList +
                ", listMap=" + listMap +
                '}';
    }
}
public class Size {
    private Integer height;
    private Integer width;

    @Override
    public String toString() {
        return "Size{" +
                "height=" + height +
                ", width=" + width +
                '}';
    }
}

3.2:测试代码

public class PropertyAccessorMain {

    public static void main(String[] args) {
        Apple apple = new Apple();
        // 使用DirectFieldAccessor
        PropertyAccessor accessor = new DirectFieldAccessor(apple);
        // 设置color字段(注意没有set写方法)
        accessor.setPropertyValue("color", "红色");

        // 设置嵌套属性(注意:此处能够正常work是因为有private Size size = new Size();,
        // 否则报错:Value of nested property 'size' is null 下同~)
        accessor.setPropertyValue("size.height", 10);

        // 设置集合/数组属性
        accessor.setPropertyValue("arrStr[0]", "arrStr");
        accessor.setPropertyValue("arrStr[1]", "arrStr1"); // 注意:虽然初始化时初始化过数组了,但是仍以此处的为准
        accessor.setPropertyValue("listStr[0]", "listStr");
        accessor.setPropertyValue("listStr[0]", "listStr1"); // 如果角标index一样,后面覆盖前面的

        // 设置Map:key只能是数值才行,否则是不好使的~~~~
        accessor.setPropertyValue("map[1]", "myValue2");

        // 设置listList这种集合里的集合
        accessor.setPropertyValue("listList[0][0]", "listList00");
        accessor.setPropertyValue("listList[0][1]", "listList01");
        // 设置listMap这种集合里面放Map
        accessor.setPropertyValue("listMap[0][0]", "listMap00");
        System.out.println(apple);
    }
}

3.3:运行

Apple{color='红色', size=Size{height=10, width=null}, arrStr=[arrStr, arrStr1], listStr=[listStr1], map={1=myValue2}, listList=[[listList00, listList01]], listMap=[{0=listMap00}]}

PropertyAccessor接近的还有一个是PropertyResolver,我们就这了解PropertyAccessor也来了解下PropertyResolver,全类名称是org.springframework.core.env.PropertyResolver,用来从外部环境(如配置文件,环境变量)解析bean中占位符,SpEL(spring expression language)的值。最后我们来看下接口中提供的方法:

org.springframework.core.env.PropertyResolver
public interface PropertyResolver {
	// 返回环境中(如properties,yml)中是否包含指定的key
	boolean containsProperty(String key);

	// 返回环境中(如properties,yml)中对应的key的value
	// 以下2个方法分别是在key不存在的是时候返回null和抛出
	// IllegalStateException
	@Nullable
	String getProperty(String key);
	String getRequiredProperty(String key) throws IllegalStateException;
	
	// 返回环境中(如properties,yml)中对应的key的value
	// 若不存在则返回defaultValue默认值
	String getProperty(String key, String defaultValue);

	// 获取指定key的值,并以Class<T>中指定的T类型返回
	// 以下2个方法是在key不存在的时候分别返回null和抛出
	// IllegalStateException
	@Nullable
	<T> T getProperty(String key, Class<T> targetType);
	<T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;

	// 获取指定key的值,并以Class<T>中指定的T类型返回
	// 如果是不存在则返回默认值
	<T> T getProperty(String key, Class<T> targetType, T defaultValue);

	// 替换text中的${...}占位符为实际值并返回替换后的结果
	// 以下2个方法分别是在${...}占位符中key不存在的时候不替换
	// 和抛出IllegalArgumentException
	String resolvePlaceholders(String text);
	String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

}

3:PropertyEditorRegistry

该接口的作用是注册java内省APIjava.beans.PropertyEditor,其中java.beans.PropertyEditor用来改变一个值从一个类型到另外一个类型,具体可以参考这里。接口路径是org.springframework.beans.PropertyEditorRegistry,源码如下:

org.springframework.beans.PropertyEditorRegistry
/*
封装相关注册用户自定义属性编辑器PropertyEditor和获取属性编辑器PropertyEditor
*/
public interface PropertyEditorRegistry {

	// 注册转换为requiredType类型的属性编辑器
	void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
	void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);

	// 获取处理指定类型requiredType的属性编辑器
	@Nullable
	PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);

}

接下来我们使用PropertyEditor的子类java.beans.PropertyEditorSupport来写一个例子。

3.1:自定义属性编辑器

/**
 * String->yudaosourcecode.selfdefinepropertyeditor.MySelfDefinePropertyEditor.MyBean
 * 的自定义属性编辑器
 */
public class MySelfDefinePropertyEditor extends PropertyEditorSupport {
    // ,分割字符串,第一个位置是age,第二个位置是name
    private String sourceText;

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        System.out.println("设置 sourceText 为:" + text);
        this.sourceText = text;
    }

    @Override
    public Object getValue() {
        MyBean myBean = new MyBean();
        String[] arr = StringUtils.commaDelimitedListToStringArray(sourceText);
        myBean.setAge(Integer.valueOf(arr[0]));
        myBean.setName(arr[1]);
        System.out.println("sourceTex 是 " + sourceText + ", 转换的结果是:" + myBean);
        return myBean;
    }

    protected static Class<MyBean> returnType() {
        return MyBean.class;
    }

    // 自己用,私有内部类即可
    private class MyBean {
        private String name;
        private int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "MyBean{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
}

3.2:测试

public class MySelfDefinePropertyEditorMain {

    public static void main(String[] args) {
        // 注册自定义属性编辑器,处理String->
        PropertyEditorRegistrySupport registrySupport
                = new PropertyEditorRegistrySupport();
        registrySupport.registerCustomEditor(MySelfDefinePropertyEditor.returnType(), new MySelfDefinePropertyEditor());

        // 获取自定义属性编辑器,并使用其进行转换工作
        PropertyEditor customEditor = registrySupport.findCustomEditor(MySelfDefinePropertyEditor.returnType(), null);
        MySelfDefinePropertyEditor selfDefinePropertyEditor = (MySelfDefinePropertyEditor)customEditor;
        selfDefinePropertyEditor.setAsText("52,张三");
        System.out.println(selfDefinePropertyEditor.getValue());
    }
}

3.3:运行

设置 sourceText 为:52,张三
sourceTex 是 52,张三, 转换的结果是:MyBean{name='张三', age=52}
MyBean{name='张三', age=52}

Process finished with exit code 0

4:TypeConverter

类名称是org.springframework.beans.TypeConverter,作用是定义类型转换的方法,源码如下:

// 定义转换方法的接口,一般和PropertyEditorRegistry一起使用(但是不是必须的)
// 因为执行具体数据转换使用的是PropertyEditor,而PropertyEditor不是线程安全的
// 因此该接口也被人为不是线程安全的,但是在spring3之后,引入信息的转换体系ConversionService
// 并且作为该接口的默认的类型转换接口,此时在spring3之后可以默认认为该接口是线程安全的
public interface TypeConverter {

	// 转换值为要求的类型(如果有必要从String转换),从String到其它任何值
	// 的转换都会调用java bean的PropertyEditor的setAsText方法,或者是
	// ConversionService的Converter接口
	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException;

	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
			@Nullable MethodParameter methodParam) throws TypeMismatchException;

	@Nullable
	<T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
			throws TypeMismatchException;

	@Nullable
	default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
			@Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {

		throw new UnsupportedOperationException("TypeDescriptor resolution not supported");
	}

}

到这里BeanWrapper就有了如下的能力:

1:基于PropertyAccessor的属性读写能力。
2:基于PropertyEditorRegistry的属性编辑器注册的能力。
3:基于TypeConverter的类型转换能力。

BeanWrapper继承自ConfigurablePropertyAccessor,该接口源码如下:

org.springframework.beans.ConfigurablePropertyAccessor
public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {

	void setConversionService(@Nullable ConversionService conversionService);

	@Nullable
	ConversionService getConversionService();

	void setExtractOldValueForEditor(boolean extractOldValueForEditor);

	boolean isExtractOldValueForEditor();

	void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);

	boolean isAutoGrowNestedPaths();

}

从源码中可以看出来,除了接口外PropertyAccessor,ProperEditorRegistry,TypeConverter,还通过setConversionServicegetConversionService集成了spring的ConversionService的类型转换体系。

5:BeanWrapper

全类名org.springframework.beans.BeanWrapper,是javabean的低级接口,提供操作javabean的属性,获取属性描述符,以及查询属性的可读写性的能力,源码如下:

// 低级javabean的核心接口,一般不会直接使用,而是通过DataBinder或者是
// BeanFactory,提供了分析和操作标准javaBean的操作,获取和设置属性值,
// 获取属性描述符的能力,获取属性的读写方法能力public interface BeanWrapper extends ConfigurablePropertyAccessor {

	// 获取封装的bean实例
	Object getWrappedInstance();

	// 获取封装的bean实例的Class类型
	Class<?> getWrappedClass();

	// 获取javabean内省API中属性对应的属性描述符
	PropertyDescriptor[] getPropertyDescriptors();

	// 获取指定属性的属性描述符
	PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;

}

BeanWrapper体系承载了Bean实例的包装,属性的设置和访问,类型的转换等重要作用,默认的实现类是org.springframework.beans.BeanWrapperImpl,在实际中也是使用的该类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值