前言
- 前一篇博客我们讲到了对BeanDefinition的解析和载入,只是找到了方法,但具体的解析过程还没分析。这是本文的主要内容。
- Spring的源码刚开始看确实很痛苦,希望大家能埋头啃一遍,对于代码阅读能力和对Spring应用的问题解决能力绝对有相当大的提升。而且你再使用的时候,脑子里面不再是就该这么用,而是这样写它会怎么解析,除了这样还可以怎么怎么写。
BeanDefinition的载入和解析
<bean>及其子节点的解析
前文讲了,这里写了所有Spring可以配置的属性、节点等等。比如我们常见的:<property>、<constructor-arg>等,也包括我们很少用的元信息(meta)、qualifier等,当然也有我们在bean里面配置的scope等。先看下简代码:
代码1.1:BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
...
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//这里面设置scope、abstract、lazy-init等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
//设置property属性
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
...
}
这里我们将两个比较有代表性的parseBeanDefinitionAttributes方法和parseConstructorArgElements方法。先来看第一个方法的代码。
代码1.2:BeanDefinitionParserDelegate类的parseBeanDefinitionAttributes方法
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
BeanDefinition containingBean, AbstractBeanDefinition bd) {
//设置scope属性
if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Specify either 'scope' or 'singleton', not both", ele);
}
}
else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?
BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
}
else if (containingBean != null) {
//设置默认,也就是空
bd.setScope(containingBean.getScope());
}
//设置abstract
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
//设置lazy-init属性
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (DEFAULT_VALUE.equals(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
//设置自动装配属性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
//设置dependency-check
String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
bd.setDependencyCheck(getDependencyCheck(dependencyCheck));
//设置depends-on
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, BEAN_NAME_DELIMITERS));
}
//设置autowire-candidate
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
//设置primary
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
//init-method
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
if (!"".equals(initMethodName)) {
bd.setInitMethodName(initMethodName);
}
}
else {
if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
}
//设置destroy-method
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
if (!"".equals(destroyMethodName)) {
bd.setDestroyMethodName(destroyMethodName);
}
}
else {
if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
}
//设置工厂方法factory-method
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
//设置工厂类factory-bean
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
上面这个方法是对<bean></bean>里面的配置,一般都是通过直接set的方式。有一点大家注意一下,赋值的是AbstractBeanDefinition抽象类,BeanDefinition是一个接口,AbstractBeanDefinition实现了该接口,并将一些赋值方法做了重写等,它是BeanDefinition的升级版。
<property>元素的解析
我们重点看parsePropertyElements方法。
代码1.3:BeanDefinitionParserDelegate类的parsePropertyElements方法
//该方法--调用入口
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//获取<Bean>元素中所有的子元素
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//如果子元素是<property>子元素,则调用解析<property>子元素方法解析
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
//该方法--解析<property>元素
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//获取<property>元素的名字
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//如果一个Bean中已经有同名的property存在,则不进行解析,直接返回。
//即如果在同一个Bean中配置同名的property,则只有第一个起作用
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析获取property的值
Object val = parsePropertyValue(ele, bd, propertyName);
//根据property的名字和值创建property实例
PropertyValue pv = new PropertyValue(propertyName, val);
//解析<property>元素中的属性
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
我们继续看下解析获取property值的方法parsePropertyValue,该方法也适用于constructor arguments(构造器参数),只不过propertyName为空就行。来看代码:
代码1.4:BeanDefinitionParserDelegate类的parsePropertyValue方法
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
String elementName = (propertyName != null) ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element";
//获取<property>的所有子元素,只能是其中一种类型:ref,value,list等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//子元素不是description和meta属性
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {//当前<property>元素包含有子元素
subElement = (Element) node;
}
}
}
//判断property的属性值是ref还是value,不允许既是ref又是value
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
//如果属性是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref信息
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
//指向运行时所依赖对象 的引用
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
//设置这个ref的数据对象是被当前的property对象所引用
ref.setSource(extractSource(ele));
return ref;
}
//如果属性是value,创建一个value的数据对象TypedStringValue,这个对象封装了value信息
else if (hasValueAttribute) {
//持有String类型值的对象 [这里一般我们都配class的路径,因为不实例化,所以这样处理]
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {//如果当前<property>元素还有子元素
return parsePropertySubElement(subElement, bd);
}
else {
// 既不是"ref"又不是"value",报错
error(elementName + " must specify a ref or value", ele);
return null;
}
}
通过对上述源码的分析,我们可以了解在Spring配置文件中,<Bean>元素中<property>元素的相关配置是如何处理的:
(1). ref被封装为指向依赖对象一个引用。
(2). value配置都会封装成一个字符串类型的对象。
(3). ref和value都通过“解析的数据类型属性值.setSource(extractSource(ele))方法将属性值/引用与所引用的属性关联起来。
(4).无论是ref/value/Collection,都只能有一个。<property>子元素的解析
在上面方法的最后对于<property>元素的子元素通过parsePropertySubElement方法解析,我们继续分析该方法的源码,了解其解析过程.
代码1.5:BeanDefinitionParserDelegate类的parsePropertySubElement方法//该方法--调用入口
public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
//该方法--解析<property>元素中ref,value或者集合等子元素
public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
//如果<property>没有使用Spring默认的命名空间,则使用用户自定义的规则解析内嵌元素
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//如果子元素是bean,则使用解析<Bean>元素的方法解析 【回到最初的那个bean解析方法】
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//如果子元素是ref,ref中只能有以下3个属性:bean、local、parent
else if (nodeNameEquals(ele, REF_ELEMENT)) {
//获取<property>元素中的bean属性值,引用其他解析的Bean的名称
//【可以不再同一个Spring配置文件中】
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
//如果没有bean,获取<property>元素中的local属性值,引用同一个Xml文件中配置的Bean的id,
//【local和ref不同,local只能引用同一个配置文件中的Bean】
refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
if (!StringUtils.hasLength(refName)) {
//如果也没有local,获取<property>元素中parent属性值,引用父级容器中的Bean的id
// 【parent只能引用同一个父级Context中的bean】.
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean', 'local' or 'parent' is required for <ref> element", ele);
return null;
}
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
/创建ref类型数据,指向被引用的对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
//如果子元素是<idref>,使用解析ref元素的方法解析
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//如果子元素是<value>,使用解析value元素的方法解析
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//如果子元素是null,为<property>设置一个封装null值的字符串数据
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
//如果子元素是<array>,使用解析array集合子元素的方法解析
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//如果子元素是<list>,使用解析list集合子元素的方法解析
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//如果子元素是<set>,使用解析set集合子元素的方法解析
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//如果子元素是<map>,使用解析map集合子元素的方法解析
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//如果子元素是<props>,使用解析props集合子元素的方法解析
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
上面的代码我们可以学到哪些东西呢?Spring对上面这些子元素的解析,也就是我们可以给property配置的全部类型。如:bean,ref,idref,value,null,以及各种集合类。对于bean和ref在上面的方法中就有,直接创建一个引用的bean对象。
<list>子元素解析
那么对于集合类型的,我们再看一个例子。
代码1.6:BeanDefinitionParserDelegate类的parsePropertySubElement方法public List parseListElement(Element collectionEle, BeanDefinition bd) {
//获取<list>元素中的value-type属性,即获取集合元素的数据类型
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
//获取<list>集合元素中的所有子节点
NodeList nl = collectionEle.getChildNodes();
//Spring中将List封装为ManagedList
ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
target.setSource(extractSource(collectionEle));
target.setElementTypeName(defaultElementType);
target.setMergeEnabled(parseMergeAttribute(collectionEle));
//具体的<list>元素解析
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
//具体解析<list>集合元素,<array>、<list>和<set>都使用该方法解析
protected void parseCollectionElements(
NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
//遍历集合所有节点
for (int i = 0; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
//节点不是description节点
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
//将解析的元素加入集合中,递归调用下一个子元素
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
}
}
}
经过对Spring Bean定义资源文件转换的Document对象中的元素层层解析,Spring IoC现在已经将XML形式定义的Bean定义资源文件转换为Spring IoC所识别的数据结构——BeanDefinition【AbstractBeanDefinition】。那么还有其他属性的解析,大家可以自己看看。一定要多看看对于元素的解析,不然依赖注入的时候你整不明白这些属性从哪里来的。
BeanDefinition载入总结
来一个代码的流程图,结合上一篇文章,顺便简单串一下主要方法。还有一个问题大家注意下,我们的代码一步步走下去是一点点细化的过程,从web.xml读取的可能是多个路径,一个路径下面可能有多个文件(Resource),一个文件里面有多个Bean,一个Bean里面有多个Property,一个Property里面只能有一个Ref,Value,List。这个过程大家要注意思考。我这里先放一张代码的流程图,再给大家举个比较复杂点的Bean的配置,大家结合着来看,可能会稍微好消化一点点。 来看一个sessionFactory的配置,找了半天觉得这个很典型,基本上Spring源码里面准备解析的都有了。来看下代码
<bean id="sessionFactory" class="com.xxx"> <!--dataSource属性指定要用到的数据源,因此在Hibernate的核心配置文件中就无需再配置与数据库连接相关的属性--> <property name="dataSource" ref="xxxx" /> <property name="hibernateProperties"> <props> <!-- Hibernate基本配置 --> <prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop> <prop key="hibernate.connection.pool_size">10</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">false</prop> </props> </property> <!-- 加入使用注解的实体类 --> <property name="annotatedClasses"> <list> <value>com.xxx.xxx</value> <value>com.xx.xxxx</value> </list> </property> </bean>
我删了很多的配置,为了项目信息保密嘛,不过这个已经很有代表性了。我们可以看到Bean的基本配置,Property三种配置都有了,我们再结合上面对bean的解析、Property的解析、list的解析。 BeanDefinition的载入内容就先这么多,以后再有想法再补充吧!后面的注册就显得相对简单了,大家如果看到这里,注册基本不是问题。