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,还通过setConversionService
,getConversionService
集成了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
,在实际中也是使用的该类。