24--Spring创建Bean的过程(六),bean属性填充解析属性值

版权声明:如有转载,请标明出处,谢谢合作! https://blog.csdn.net/lyc_liyanchao/article/details/82808427

在上一小节中,我们已经分析了Spring创建bean的时候,对bean属性填充的简要过程,这一过程是相当复杂的。

  1. 对于bean中的属性,可能有String,int,甚至数组,List,Map,Set等等,那么Spring是如何通过解析beanDefinition从而将其转换为合适的类型呢?
  2. 我们之前讲过Spring可以通过Setter方法注入属性,那么这个加入bean中有name属性,name属性存在setName方法,这个方法何时会被调用呢?

答案就在bean属性填充的过程中,本过程是比较复杂枯燥的,例如对bean属性的类型转换,String,int,数组,List,Map,Set等等,都需要单独去进行判断,接下来我们通过源码来分析其是如何解析,如何被注入到bean实例中的。

#####1.新建测试

  • bean
package com.lyc.cn.day12;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:36
 */
public interface Animal {
	void sayHello();
}
package com.lyc.cn.day12;

import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:36
 */
public class Cat implements Animal {
	/** 姓名 **/
	private String name;

	/** 年龄 **/
	private int age;

	/** 注入List集合 **/
	private List<String> listNames;

	/***注入Set集合*/
	private Set<String> setNames;

	/** 注入Properties **/
	private Properties propertiesNames;

	/** 注入Map集合 **/
	private Map<String, String> mapNames;

	/** 注入数组 **/
	private String[] arrayNames;

	/** 无参构造函数 **/
	public Cat() {

	}

	/**
	 * 构造函数
	 * @param name 姓名
	 * @param age  年龄
	 */
	public Cat(String name, int age) {
		this.name = name;
		this.age = age;
	}

	/**
	 * 打印数组集合
	 */
	public void sayArrayNames() {
		System.out.println("注入数组-->");
		if (arrayNames != null && arrayNames.length > 0) {
			for (int i = 0; i < arrayNames.length; i++) {
				System.out.println(arrayNames[i]);
			}
		}
		System.out.println();
	}

	/**
	 * 打印Map集合
	 */
	public void sayMapNames() {
		if (null != mapNames && mapNames.size() > 0) {
			System.out.println("注入Map集合-->");
			for (Map.Entry<String, String> entry : mapNames.entrySet()) {
				System.out.println("key= " + entry.getKey() + " value= " + entry.getValue());
			}
			System.out.println();
		}
	}

	/**
	 * 打印Properties属性
	 */
	public void sayPropertiesNames() {
		if (null != propertiesNames) {
			System.out.println("注入properties属性-->");
			System.out.println(propertiesNames.get("name"));
			System.out.println(propertiesNames.get("age"));
			System.out.println();
		}
	}

	/**
	 * 打印List集合
	 */
	public void sayListNames() {
		if (!CollectionUtils.isEmpty(listNames)) {
			System.out.println("注入List集合-->");
			for (String string : listNames) {
				System.out.println(string);
			}
			System.out.println();
		}
	}

	/**
	 * 打印Set集合
	 */
	public void saySetNames() {
		if (!CollectionUtils.isEmpty(setNames)) {
			System.out.println("注入Set集合-->");
			Iterator<String> iterator = setNames.iterator();
			while (iterator.hasNext()) {
				System.out.println(iterator.next());
			}
			System.out.println();
		}
	}

	@Override
	public void sayHello() {
		System.out.println("大家好, 我叫" + getName() + ", 我今年" + getAge() + "岁了\n");
	}

	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;
	}

	public List<String> getListNames() {
		return listNames;
	}

	public void setListNames(List<String> listNames) {
		this.listNames = listNames;
	}

	public Set<String> getSetNames() {
		return setNames;
	}

	public void setSetNames(Set<String> setNames) {
		this.setNames = setNames;
	}

	public Properties getPropertiesNames() {
		return propertiesNames;
	}

	public void setPropertiesNames(Properties propertiesNames) {
		this.propertiesNames = propertiesNames;
	}

	public Map<String, String> getMapNames() {
		return mapNames;
	}

	public void setMapNames(Map<String, String> mapNames) {
		this.mapNames = mapNames;
	}

	public String[] getArrayNames() {
		return arrayNames;
	}

	public void setArrayNames(String[] arrayNames) {
		this.arrayNames = arrayNames;
	}
}
  • 配置文件
<?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 id="cat" class="com.lyc.cn.day12.Cat">

		<!--注入String-->
		<property name="name" value="美美"/>
		<!--注入int-->
		<property name="age" value="3"/>

		<!--注入List集合-->
		<property name="listNames">
			<!-- merge 父子bean是否合并条目 -->
			<list value-type="java.lang.String" merge="false">
				<value>张三</value>
				<value>李四</value>
				<value>王五</value>
			</list>
		</property>

		<!--注入Set集合-->
		<property name="setNames">
			<set value-type="java.lang.String" merge="true">
				<value>张三</value>
				<value>李四</value>
				<value>王五</value>
			</set>
		</property>

		<!--注入Map集合-->
		<property name="mapNames">
			<map key-type="java.lang.String" value-type="java.lang.String">
				<entry key="name" value="小明"/>
				<entry key="age" value="3"/>
			</map>
		</property>

		<!--注入数组-->
		<property name="arrayNames">
			<array value-type="java.lang.String">
				<value>张三</value>
				<value>李四</value>
				<value>王五</value>
			</array>
		</property>

		<!--注入Properties-->
		<property name="propertiesNames">
			<props value-type="java.lang.String">
				<prop key="name">小明</prop>
				<prop key="age">3</prop>
			</props>
		</property>
	</bean>
</beans>
  • 测试类
package com.lyc.cn.day12;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;

/**
 * @author: LiYanChao
 * @create: 2018-09-07 16:43
 */
public class MyTest {
	private XmlBeanFactory xmlBeanFactory;


	@Before
	public void initXmlBeanFactory() {
		System.out.println("========测试方法开始=======\n");
		xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("day12.xml"));
	}

	@After
	public void after() {
		System.out.println("\n========测试方法结束=======");
	}

	@Test
	public void test() {
		Cat cat = xmlBeanFactory.getBean("cat",Cat.class);
		cat.sayHello();
		cat.sayArrayNames();
		cat.sayListNames();
		cat.sayMapNames();
		cat.sayPropertiesNames();
		cat.saySetNames();
	}

}
========测试方法开始=======

大家好, 我叫美美, 我今年3岁了

注入数组-->
张三
李四
王五

注入List集合-->
张三
李四
王五

注入Map集合-->
key= name value= 小明
key= age value= 3

注入properties属性-->
小明
3

注入Set集合-->
张三
李四
王五


========测试方法结束======

这个测试类,我们在前面的章节中已经分析过,分别注入了 String,int,List,Set,Map,Properties等属性,接下来我们来分析其是如何被注入的,这个过程相当复杂,一个小节是讲不完的,这一小节,我们先来分析属性值的解析过程。

2.属性填充过程简析
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    // ① 如果PropertyValues为空,直接返回
    if (pvs.isEmpty()) {
        return;
    }

    // ②判断安全管理器
    if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
        ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
    }

    //MutablePropertyValues是PropertyValues接口的默认实现类
    MutablePropertyValues mpvs = null;
    List<PropertyValue> original;

    // ③ 获取bean的属性集合
    // 如果pvs是MutablePropertyValues的实例,MutablePropertyValues是PropertyValues的默认实现
    if (pvs instanceof MutablePropertyValues) {
        // 将pvs转换为MutablePropertyValues对象,并判断mpvs是否已经经过转换
        mpvs = (MutablePropertyValues) pvs;
        if (mpvs.isConverted()) {
            // 如果pvs已经转换过,则直接设置属性值无需再次转换
            try {
                bw.setPropertyValues(mpvs);
                return;
            }
            catch (BeansException ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex);
            }
        }
        // 否则获取原始PropertyValue集合
        original = mpvs.getPropertyValueList();
    }
    else {
        original = Arrays.asList(pvs.getPropertyValues());
    }

    // ④ 获取类型转换器
    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
        converter = bw;
    }
    BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

    // ⑤ 通过深度拷贝,解析值引用
    List<PropertyValue> deepCopy = new ArrayList<>(original.size());
    boolean resolveNecessary = false;
    // 循环解析PropertyValues
    for (PropertyValue pv : original) {
        if (pv.isConverted()) {
            deepCopy.add(pv);
        }
        else {
            // 获取属性名称
            String propertyName = pv.getName();
            // 获取原始属性值
            Object originalValue = pv.getValue();
            // 解析原始属性值
            // 当注入集合属性时,如果指定了,value-type,如value-type="java.lang.String",
            // 那么resolveValueIfNecessary也会执行类型的转换操作
            Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
            Object convertedValue = resolvedValue;
            // isWritableProperty-->判断属性是否可写,如果属性不存在返回false
            // isNestedOrIndexedProperty-->判断是否索引属性或者嵌套属性
            boolean convertible = bw.isWritableProperty(propertyName) && !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
            if (convertible) {
                // 类型转换
                convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
            }
            // Possibly store converted value in merged bean definition,in order to avoid re-conversion for every created bean instance.
            // ⑥缓存已经转换过的值,避免再次转换
            if (resolvedValue == originalValue) {
                if (convertible) {
                    pv.setConvertedValue(convertedValue);
                }
                deepCopy.add(pv);
            }
            else if (convertible && originalValue instanceof TypedStringValue &&
                    !((TypedStringValue) originalValue).isDynamic() &&
                    !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
                pv.setConvertedValue(convertedValue);
                deepCopy.add(pv);
            }
            else {
                resolveNecessary = true;
                deepCopy.add(new PropertyValue(pv, convertedValue));
            }
        }
    }

    if (mpvs != null && !resolveNecessary) {
        mpvs.setConverted();
    }

    // ⑦设置属性值.
    try {
        bw.setPropertyValues(new MutablePropertyValues(deepCopy));
    }
    catch (BeansException ex) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex);
    }
}

applyPropertyValues方法进行属性填充,大致经历了上面七个步骤:

① 如果PropertyValues为空,直接返回
②判断安全管理器
③ 如果pvs是MutablePropertyValues的实例,则尝试获取已经转换过的值,否则获取原始属性值
④ 获取类型转换器
⑤ 通过深度拷贝,解析值引用
⑥缓存已经转换过的值,避免再次转换
⑦设置属性值
上面的步骤中,⑤和⑦比较重要也比较复杂,其他的步骤相信大家都能看懂,我们重点分析第⑤和第⑦步。

3.解析值引用

到第⑤步的时候,我们没能通过获取已经转换过的值直接设置属性,而是拿到了原始值集合,这里就要对原始值进行转换了。这也是我们提到的第一个问题的答案所在之处。这里大致分为两个步骤,1.解析值引用 2.转换值引用。我们先来分析解析值引用

注意:当注入如List,Set属性,如果配置文件中配置了value-type属性,那么在解析值引用的同时会将集合中的值进行转换。例如我们上面的配置:

<!--注入List集合-->
<property name="listNames">
    <!-- merge 父子bean是否合并条目 -->
    <list value-type="java.lang.String" merge="false">
        <value>张三</value>
        <value>李四</value>
        <value>王五</value>
    </list>
</property>

因为整个过程比较复杂,所以我们说明一下,避免同学们搞不清值解析和值转换的时机。

我们继续来看值解析的代码:

public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
    // We must check each value to see whether it requires a runtime reference to another bean to be resolved.

    // ① RuntimeBeanReference->运行时引用
    //   例如BeanA依赖BeanB,那么在配置文件中有通过配置ref标签进行引用的,在解析BeanDefinition的时候,是不会直接实例化BeanB的,那么这个引用就是RuntimeBeanReference
    if (value instanceof RuntimeBeanReference) {
        RuntimeBeanReference ref = (RuntimeBeanReference) value;
        return resolveReference(argName, ref);
    }
    // ② RuntimeBeanNameReference->没弄明白
    else if (value instanceof RuntimeBeanNameReference) {
        String refName = ((RuntimeBeanNameReference) value).getBeanName();
        refName = String.valueOf(doEvaluate(refName));
        if (!this.beanFactory.containsBean(refName)) {
            throw new BeanDefinitionStoreException("Invalid bean name '" + refName + "' in bean reference for " + argName);
        }
        return refName;
    }
    // ③ 解析innerBean
    else if (value instanceof BeanDefinitionHolder) {
        // Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
        BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
        return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
    }
    else if (value instanceof BeanDefinition) {
        // Resolve plain BeanDefinition, without contained name: use dummy name.
        BeanDefinition bd = (BeanDefinition) value;
        String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(bd);
        return resolveInnerBean(argName, innerBeanName, bd);
    }
    // ④ 解析数组
    else if (value instanceof ManagedArray) {
        // May need to resolve contained runtime references.
        ManagedArray array = (ManagedArray) value;
        Class<?> elementType = array.resolvedElementType;
        if (elementType == null) {
            String elementTypeName = array.getElementTypeName();
            if (StringUtils.hasText(elementTypeName)) {
                try {
                    elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
                    array.resolvedElementType = elementType;
                }
                catch (Throwable ex) {
                    // Improve the message by showing the context.
                    throw new BeanCreationException(this.beanDefinition.getResourceDescription(),this.beanName,"Error resolving array type for "+argName, ex);
                }
            }
            else {
                elementType = Object.class;
            }
        }
        return resolveManagedArray(argName, (List<?>) value, elementType);
    }
    // ⑤ 解析List集合
    else if (value instanceof ManagedList) {
        // May need to resolve contained runtime references.
        return resolveManagedList(argName, (List<?>) value);
    }
    // ⑥ 解析Set集合
    else if (value instanceof ManagedSet) {
        // May need to resolve contained runtime references.
        return resolveManagedSet(argName, (Set<?>) value);
    }
    // ⑦ 解析Map集合
    else if (value instanceof ManagedMap) {
        // May need to resolve contained runtime references.
        return resolveManagedMap(argName, (Map<?, ?>) value);
    }
    // ⑧ 解析Properties集合
    else if (value instanceof ManagedProperties) {
        Properties original = (Properties) value;
        Properties copy = new Properties();
        original.forEach((propKey, propValue) -> {
            if (propKey instanceof TypedStringValue) {
                propKey = evaluate((TypedStringValue) propKey);
            }
            if (propValue instanceof TypedStringValue) {
                propValue = evaluate((TypedStringValue) propValue);
            }
            if (propKey == null || propValue == null) {
                throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,
                        "Error converting Properties key/value pair for " + argName + ": resolved to null");
            }
            copy.put(propKey, propValue);
        });
        return copy;
    }
    // ⑨ 解析字符串
    else if (value instanceof TypedStringValue) {
        // Convert value to target type here.
        TypedStringValue typedStringValue = (TypedStringValue) value;
        Object valueObject = evaluate(typedStringValue);
        try {
            Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
            if (resolvedTargetType != null) {
                return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
            }
            else {
                return valueObject;
            }
        }
        catch (Throwable ex) {
            // Improve the message by showing the context.
            throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,
                    "Error converting typed String value for " + argName, ex);
        }
    }
    // ⑩ NullBean或者表达式
    else if (value instanceof NullBean) {
        return null;
    }
    else {
        return evaluate(value);
    }
}

针对value的不同有不同的解析方式

① RuntimeBeanReference->运行时引用,如果两个Bean之间存在着依赖关系,在配置文件中如果通过Setter方法注入了一方的实例属性,就是RuntimeBeanReference了,这里还包含了对循环引用的处理,我们在下面的章节详细讲解
② RuntimeBeanNameReference->没弄明白,从名字上看来跟RuntimeBeanReference相似,但是没弄清楚其使用方式。
③ 解析innerBean,BeanDefinitionHolder和BeanDefinition两种类型用于解析内部bean,当我们希望某个bean只能服务于一个指定bean时,这时就需要用到内部bean,内部bean只能被其依赖的bean使用,在程序中无法通过getBean方法获取其实例。
④ 解析数组
⑤ 解析List集合
⑥ 解析Set集合
⑦ 解析Map集合
⑧ 解析Properties集合
⑨ 解析字符串,如果通过上述解析无效的话,此时value值会被当做字符串来处理,注意:当做字符串来处理,并不代表value值注入到bean中的最终类型,其有可能是int,float等类型。
⑩ NullBean或者表达式,对表达式的解析,没弄明白,也没找到其使用场景。

下面我们针对开发中常用的属性解析逐步分析

3.解析数组

创建长度为配置文件中指定的元素个数的数组,循环元素并在此调用resolveValueIfNecessary解析元素。

private Object resolveManagedArray(Object argName, List<?> ml, Class<?> elementType) {
    // 创建长度为配置文件中指定的元素个数的数组
    Object resolved = Array.newInstance(elementType, ml.size());
    // 循环解析数组中的元素,如果配置文件中指定了value-type,resolveValueIfNecessary过程中会对元素进行类型转换
    for (int i = 0; i < ml.size(); i++) {
        Array.set(resolved, i,resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));
    }
    return resolved;
}
4.解析List

与解析数组的过程相似。

private List<?> resolveManagedList(Object argName, List<?> ml) {
    // 创建长度为配置文件中指定的元素个数的List集合
    List<Object> resolved = new ArrayList<>(ml.size());
    for (int i = 0; i < ml.size(); i++) {
        // 循环解析元素,如果配置文件中指定了value-type,resolveValueIfNecessary过程中会对元素进行类型转换
        resolved.add(resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));
    }
    return resolved;
}
5.解析Set

与解析数组的过程相似。

private Set<?> resolveManagedSet(Object argName, Set<?> ms) {
    // 创建长度为配置文件中指定的元素个数的Set集合
    Set<Object> resolved = new LinkedHashSet<>(ms.size());
    int i = 0;
    for (Object m : ms) {
        // 循环解析元素,如果配置文件中指定了value-type,resolveValueIfNecessary过程中会对元素进行类型转换
        resolved.add(resolveValueIfNecessary(new KeyedArgName(argName, i), m));
        i++;
    }
    return resolved;
}
6.解析Map

与解析数组的过程相似。

private Map<?, ?> resolveManagedMap(Object argName, Map<?, ?> mm) {
    // 创建长度为配置文件中指定的元素个数的Map集合
    Map<Object, Object> resolved = new LinkedHashMap<>(mm.size());
    // 循环解析元素,如果配置文件中指定了value-type,resolveValueIfNecessary过程中会对元素进行类型转换
    mm.forEach((key, value) -> {
        Object resolvedKey = resolveValueIfNecessary(argName, key);
        Object resolvedValue = resolveValueIfNecessary(new KeyedArgName(argName, key), value);
        resolved.put(resolvedKey, resolvedValue);
    });
    return resolved;
}

猜你喜欢

转载自blog.csdn.net/lyc_liyanchao/article/details/82808427