《Spring源码深度解析》学习笔记——默认标签的解析
来源:互联网 发布:网络推广电话销售 编辑:程序博客网 时间:2024/05/18 11:28
默认标签的解析
Spring中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在着很大的不同
默认标签的解析是在parseDefaultElement函数中进行的,分别对4种不同标签(import、alias、bean和beans)做了不同的处理
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if(delegate.nodeNameEquals(ele, "import")) { this.importBeanDefinitionResource(ele); } else if(delegate.nodeNameEquals(ele, "alias")) { this.processAliasRegistration(ele); } else if(delegate.nodeNameEquals(ele, "bean")) { this.processBeanDefinition(ele, delegate); }}
对bean标签的解析及注册
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if(bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException var5) { this.getReaderContext().error("Failed to register bean definition with name \'" + bdHolder.getBeanName() + "\'", ele, var5); } this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); }}
大致逻辑如下:
- 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,经过这个方法后,bdHolder实例已经包含我们配置文件中配置的各种属性了,例如class、name、id、alias之类的属性
- 当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析
- 解析完成后,需要对解析后bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法
- 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了
解析BeanDefinition
首先是元素解析及信息提取,BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele),进入BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法
//BeanDefinitionParserDelegate.javapublic BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return this.parseBeanDefinitionElement(ele, (BeanDefinition)null);}public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { //解析id属性 String id = ele.getAttribute("id"); //解析name属性 String nameAttr = ele.getAttribute("name"); //分割name属性 ArrayList aliases = new ArrayList(); if(StringUtils.hasLength(nameAttr)) { String[] beanName = StringUtils.tokenizeToStringArray(nameAttr, ",; "); aliases.addAll((Collection)Arrays.asList(beanName)); } String beanName1 = id; if(!StringUtils.hasText(id) && !aliases.isEmpty()) { beanName1 = (String)aliases.remove(0); if(this.logger.isDebugEnabled()) { this.logger.debug("No XML \'id\' specified - using \'" + beanName1 + "\' as bean name and " + aliases + " as aliases"); } } if(containingBean == null) { this.checkNameUniqueness(beanName1, aliases, ele); } AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName1, containingBean); if(beanDefinition != null) { if(!StringUtils.hasText(beanName1)) { try { //如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的beanName if(containingBean != null) { beanName1 = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true); } else { beanName1 = this.readerContext.generateBeanName(beanDefinition); String aliasesArray = beanDefinition.getBeanClassName(); if(aliasesArray != null && beanName1.startsWith(aliasesArray) && beanName1.length() > aliasesArray.length() && !this.readerContext.getRegistry().isBeanNameInUse(aliasesArray)) { aliases.add(aliasesArray); } } if(this.logger.isDebugEnabled()) { this.logger.debug("Neither XML \'id\' nor \'name\' specified - using generated bean name [" + beanName1 + "]"); } } catch (Exception var9) { this.error(var9.getMessage(), ele); return null; } } String[] aliasesArray1 = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName1, aliasesArray1); } else { return null; }}
在当前层完成的主要工作包括如下内容:
(1)提取元素中的id以及name属性
(2)进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中
(3)如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName
(4)将获取到的信息封装到BeanDefinitionHolder的实例中
步骤(2)中对标签其他属性的解析过程如下
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; //解析class属性 if(ele.hasAttribute("class")) { className = ele.getAttribute("class").trim(); } try { String ex = null; //解析parent属性 if(ele.hasAttribute("parent")) { ex = ele.getAttribute("parent"); } //创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition AbstractBeanDefinition bd = this.createBeanDefinition(className, ex); //硬编码解析默认bean的各种属性 this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); //提取description bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description")); //解析元数据 this.parseMetaElements(ele, bd); //解析lookup-method属性 this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //解析replaced-method属性 this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析构造函数参数 this.parseConstructorArgElements(ele, bd); //解析property子元素 this.parsePropertyElements(ele, bd); //解析qualifier子元素 this.parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(this.extractSource(ele)); AbstractBeanDefinition var8 = bd; return var8; } catch (ClassNotFoundException var13) { this.error("Bean class [" + className + "] not found", ele, var13); } catch (NoClassDefFoundError var14) { this.error("Class that bean class [" + className + "] depends on not found", ele, var14); } catch (Throwable var15) { this.error("Unexpected failure during bean definition parsing", ele, var15); } finally { this.parseState.pop(); } return null;}
下面继续一些复杂标签属性的解析
1.创建用于属性承载的BeanDefinition
BeanDefinition是一个接口,在Spring中存在三种实现:RootBeanDefinition、ChildBeanDefinition以及GenericBeanDefinition。三种实现均继承了AbstractBeanDefinition,其中BeanDefinition是配置文件元素标签在容器中的内部表示形式。元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的元素标签,GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类
在配置文件中可以定义父和子,父用RootBeanDefinition表示,而子用ChildBeanDefinition表示,而没有父的就使用RootBeanDefinition表示,AbstractBeanDefinition对两者共同的类信息进行抽象
Spring通过BeanDefinition将配置文件中的配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器中的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息,它们之间的关系如图所示
由此可知,要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例,而代码createBeanDefinition(className, parent)的作用就是实现此功能
protected AbstractBeanDefinition createBeanDefinition(String className, String parentName) throws ClassNotFoundException { return BeanDefinitionReaderUtils.createBeanDefinition(parentName, className, this.readerContext.getBeanClassLoader());}//BeanDefinitionReaderUtils.java public static AbstractBeanDefinition createBeanDefinition(String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException { GenericBeanDefinition bd = new GenericBeanDefinition(); //parentName可能为空 bd.setParentName(parentName); if(className != null) { if(classLoader != null) { //如果ClassLoader不为空,则使用以传入ClassLoader同一虚拟机加载类对象,否则只是记录className bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { bd.setBeanClassName(className); } } return bd;}
2.解析各种属性
parseBeanDefinitionAttributes方法是对element所有元素属性进行解析
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) { //解析scope属性 if(ele.hasAttribute("scope")) { bd.setScope(ele.getAttribute("scope")); if(ele.hasAttribute("singleton")) { //scope与singleton两个属性只能指定其中之一,不可以同时出现,否则Spring将会报出异常 this.error("Specify either \'scope\' or \'singleton\', not both", ele); } //解析singleton属性 } else if(ele.hasAttribute("singleton")) { bd.setScope("true".equals(ele.getAttribute("singleton"))?"singleton":"prototype"); } else if(containingBean != null) { //在嵌入BeanDefinition情况下且没有单独指定scope属性则使用父类默认的属性 bd.setScope(containingBean.getScope()); } //解析abstract属性 if(ele.hasAttribute("abstract")) { bd.setAbstract("true".equals(ele.getAttribute("abstract"))); } //解析lazy-init属性 String lazyInit = ele.getAttribute("lazy-init"); if("default".equals(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } //若没有设置或设置成其他字符都被会设置为false bd.setLazyInit("true".equals(lazyInit)); //解析autowire属性 String autowire = ele.getAttribute("autowire"); bd.setAutowireMode(this.getAutowireMode(autowire)); //解析dependency-check属性 String dependencyCheck = ele.getAttribute("dependency-check"); bd.setDependencyCheck(this.getDependencyCheck(dependencyCheck)); String autowireCandidate; //解析depends-on属性 if(ele.hasAttribute("depends-on")) { autowireCandidate = ele.getAttribute("depends-on"); bd.setDependsOn(StringUtils.tokenizeToStringArray(autowireCandidate, ",; ")); } //解析autowire-candidate autowireCandidate = ele.getAttribute("autowire-candidate"); String destroyMethodName; if(!"".equals(autowireCandidate) && !"default".equals(autowireCandidate)) { bd.setAutowireCandidate("true".equals(autowireCandidate)); } else { destroyMethodName = this.defaults.getAutowireCandidates(); if(destroyMethodName != null) { String[] patterns = StringUtils.commaDelimitedListToStringArray(destroyMethodName); bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); } } //解析primary属性 if(ele.hasAttribute("primary")) { bd.setPrimary("true".equals(ele.getAttribute("primary"))); } //解析init-method属性 if(ele.hasAttribute("init-method")) { destroyMethodName = ele.getAttribute("init-method"); if(!"".equals(destroyMethodName)) { bd.setInitMethodName(destroyMethodName); } } else if(this.defaults.getInitMethod() != null) { bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } //解析destroy-method属性 if(ele.hasAttribute("destroy-method")) { destroyMethodName = ele.getAttribute("destroy-method"); 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")) { bd.setFactoryMethodName(ele.getAttribute("factory-method")); } //解析factory-bean属性 if(ele.hasAttribute("factory-bean")) { bd.setFactoryBeanName(ele.getAttribute("factory-bean")); } return bd;}
3.解析子元素meta
元数据meta属性的使用
<bean id="myTestBean" class="bean.MyTestBean"> <meta key="testStr" value="aaaaaaa"></bean>
当需要使用meta里面的信息的时候可以通过BeanDefinition的getAttribute(key)方法进行获取
对meta属性的解析代码如下:
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) { //获取当前节点的所有子元素 NodeList nl = ele.getChildNodes(); for(int i = 0; i < nl.getLength(); ++i) { Node node = nl.item(i); //提取meta if(node instanceof Element && this.nodeNameEquals(node, "meta")) { Element metaElement = (Element)node; String key = metaElement.getAttribute("key"); String value = metaElement.getAttribute("value"); //使用key、value构造BeanMetadataAttribute BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value); attribute.setSource(this.extractSource(metaElement)); //记录信息 attributeAccessor.addMetadataAttribute(attribute); } }}
4.解析子元素lookup-method
lookup-method通常称为获取器注入,是一种特殊的方法注入,它是把一个方法声明为返回某种类型的bean,但实际要返回的bean是在配置文件里面配置的,此方法可用在设计有些可插拔的功能上,解除程序依赖,具体应用如下
(1)首先创建一个父类
package test.lookup.bean;public class User{ public void showMe(){ System.out.println("i am User"); }}
(2)创建其子类并覆盖showMe方法
package test.lookup.bean;public class Teacher extends User{ public void showMe(){ System.out.println("i am Teacher"); }}
(3)创建调用方法
public abstract class GetBeanTest{ public void showMe(){ this.getBean().showMe(); } public abstract User getBean();}
(4)创建测试方法
package test.lookup;import org.Springframework.context.ApplicationContext;import org.Springframework.context.support.ClassPathXmlApplicationContext;import test.lookup.app.GetBeanTest;public class Main{ public static void main(String[] args){ ApplicationContext bf = new ClassPathXmlApplicationContext("test/lookupTest.xml"); GetBeanTest test = (GetBeanTest)bf.getBean("getBeanTest"); test.showMe(); }}
抽象方法的实现是在Spring为我们提供的获取器中,配置文件的配置如下
<?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="getBeanTest" class="test.lookup.app.GetBeanTest"> <lookup-method name="getBean" bean="teacher"/> </bean> <bean id="teacher" class="test.lookup.bean.Teacher"/></beans>
在配置文件中,可以看到源码解析中提到的lookup-method子元素,这个配置完成的功能是动态地将teacher所代表的bean作为getBean的返回值
再次看Spring属性提取源码
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) { NodeList nl = beanEle.getChildNodes(); for(int i = 0; i < nl.getLength(); ++i) { Node node = nl.item(i); //仅当在Spring默认bean的子元素下且为<lookup-method>时有效 if(node instanceof Element && this.nodeNameEquals(node, "lookup-method")) { Element ele = (Element)node; //获取要修饰的方法 String methodName = ele.getAttribute("name"); //获取配置返回的bean String beanRef = ele.getAttribute("bean"); LookupOverride override = new LookupOverride(methodName, beanRef); override.setSource(this.extractSource(ele)); overrides.addOverride(override); } }}
上述代码在数据存储上面通过使用LookupOverride类型的实体类来进行数据承载并记录在AbstractBeanDefinition中的methodOverrides属性中
5.解析子元素replaced-method
这个方法主要是对bean中replaced-method子元素的提取,在开始提取分析之前先介绍下这个元素的用法
方法替换:可以在运行时用新的方法替换现有的方法,与之前的look-up不同的是,replaced-method不但可以动态地替换返回实体bean,而且还能动态地更改原有方法的逻辑,示例如下
(1)在changeMe中完成某个业务逻辑
public class TestChangeMethod{ public void changeMe(){ System.out.println("changeMe"); }}
(2)在运营一段时间后需要改变原有的业务逻辑
public class TestMethodReplacer implements MethodReplacer{ @Override public Object reimplement(Object obj, Method method, Object[] args) throws Throwable{ System.out.println("我替换了原有的方法"); return null; }}
(3)使替换后的类生效
<?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="testChangeMethod" class="test.replacemethod.TestChangeMethod"> <replaced-method name="changeMe" replacer="replacer"> </bean> <bean id="replacer" class="test.replacemethod.TestMethodReplacer"/></beans>
(4)测试
public static void main(String[] args){ ApplicationContext bf = new ClassPathXmlApplicationContext("test/replacemethod/replaceMethodTest.xml"); TestChangeMethod test = (TestChangeMethod) bf.getBean("testChangeMethod"); test.changeMe();}
运行测试类,控制台打印出”我替换了原有的方法”,也就是做到了动态替换原有方法,再次来看Spring中元素的提取过程:
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) { NodeList nl = beanEle.getChildNodes(); for(int i = 0; i < nl.getLength(); ++i) { Node node = nl.item(i); //仅当在Spring默认bean的子元素下且为<replaced-method>时有效 if(node instanceof Element && this.nodeNameEquals(node, "replaced-method")) { Element replacedMethodEle = (Element)node; //提取要替换的旧的方法 String name = replacedMethodEle.getAttribute("name"); //提取对应的新的替换方法 String callback = replacedMethodEle.getAttribute("replacer"); ReplaceOverride replaceOverride = new ReplaceOverride(name, callback); List argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, "arg-type"); Iterator var12 = argTypeEles.iterator(); while(var12.hasNext()) { Element argTypeEle = (Element)var12.next(); replaceOverride.addTypeIdentifier(argTypeEle.getAttribute("match")); } replaceOverride.setSource(this.extractSource(replacedMethodEle)); overrides.addOverride(replaceOverride); } }}
无论是look-up还是replaced-method都是构造了一个MethodOverride,并最终记录在了AbstractBeanDefinition中的methodOverrides属性中
6.解析子元素constructor-arg
......<beans><!-- 默认的情况是按照参数的顺序注入,当指定index索引后就可以改变注入参数的顺序 --><bean id="helloBean" class="com.HelloBean"> <constructor-arg index="0"> <value>张三</value> </constructor-arg> <constructor-arg index="1"> <value>李四</value> </constructor-arg></bean>......</beans>
上面的配置是Spring构造函数配置中最基础的配置,实现的功能就是对HelloBean自动寻找对应的构造函数,并在初始化的时候将设置的参数传入进去
对于constructor-arg子元素的解析,Spring通过parseConstructorArgElements函数来实现的,具体的代码如下:
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) { NodeList nl = beanEle.getChildNodes(); for(int i = 0; i < nl.getLength(); ++i) { Node node = nl.item(i); if(node instanceof Element && this.nodeNameEquals(node, "constructor-arg")) { this.parseConstructorArgElement((Element)node, bd); } }}
遍历所有子元素,提取所有constructor-arg,然后进行解析
public void parseConstructorArgElement(Element ele, BeanDefinition bd) { //提取index的属性 String indexAttr = ele.getAttribute("index"); //提取type的属性 String typeAttr = ele.getAttribute("type"); //提取name的属性 String nameAttr = ele.getAttribute("name"); if(StringUtils.hasLength(indexAttr)) { try { int value = Integer.parseInt(indexAttr); if(value < 0) { this.error("\'index\' cannot be lower than 0", ele); } else { try { this.parseState.push(new ConstructorArgumentEntry(value)); //解析ele对应的属性元素 Object valueHolder = this.parsePropertyValue(ele, bd, (String)null); ValueHolder valueHolder1 = new ValueHolder(valueHolder); if(StringUtils.hasLength(typeAttr)) { valueHolder1.setType(typeAttr); } if(StringUtils.hasLength(nameAttr)) { valueHolder1.setName(nameAttr); } valueHolder1.setSource(this.extractSource(ele)); if(bd.getConstructorArgumentValues().hasIndexedArgumentValue(value)) { this.error("Ambiguous constructor-arg entries for index " + value, ele); } else { bd.getConstructorArgumentValues().addIndexedArgumentValue(value, valueHolder1); } } finally { this.parseState.pop(); } } } catch (NumberFormatException var18) { this.error("Attribute \'index\' of tag \'constructor-arg\' must be an integer", ele); } } else { try { this.parseState.push(new ConstructorArgumentEntry()); Object value1 = this.parsePropertyValue(ele, bd, (String)null); ValueHolder valueHolder2 = new ValueHolder(value1); if(StringUtils.hasLength(typeAttr)) { valueHolder2.setType(typeAttr); } if(StringUtils.hasLength(nameAttr)) { valueHolder2.setName(nameAttr); } valueHolder2.setSource(this.extractSource(ele)); bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder2); } finally { this.parseState.pop(); } }}
上述代码的逻辑并不复杂,首先是提取constructor-arg上必要的属性(index、type、name)
如果配置中指定了index属性,那么操作步骤如下
- 解析constructor-arg的子元素
- 使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
- 将type、name和index属性一并封装在ConstructorArgumentValues.ValueHolder类型中并添加至当前BeanDefinition的constructorArgumentValues的indexedArgumentValues属性中
如果没有指定index属性,那么操作步骤如下
- 解析constructor-arg的子元素
- 使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
- 将type、name和index属性一并封装在ConstructorArgumentValues.ValueHolder类型中并添加至当前BeanDefinition的ConstructorArgumentValues的genericArgumentValues属性中
可以看到,对于是否制定index属性来讲,Spring的处理流程是不同的,关键在于属性信息被保存的位置
下面进一步了解解析构造函数配置中子元素的过程,进入parsePropertyValue
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) { String elementName = propertyName != null?"<property> element for property \'" + propertyName + "\'":"<constructor-arg> element"; //一个属性只能对应一种类型:ref、value、list等 NodeList nl = ele.getChildNodes(); Element subElement = null; for(int hasRefAttribute = 0; hasRefAttribute < nl.getLength(); ++hasRefAttribute) { Node hasValueAttribute = nl.item(hasRefAttribute); //对应description或者meta不处理 if(hasValueAttribute instanceof Element && !this.nodeNameEquals(hasValueAttribute, "description") && !this.nodeNameEquals(hasValueAttribute, "meta")) { if(subElement != null) { this.error(elementName + " must not contain more than one sub-element", ele); } else { subElement = (Element)hasValueAttribute; } } } //解析constructor-arg上的ref属性 boolean var11 = ele.hasAttribute("ref"); //解析constructor-arg上的value属性 boolean var12 = ele.hasAttribute("value"); if(var11 && var12 || (var11 || var12) && subElement != null) { /* *在constructor-arg上不存在: * 1.同时既有ref属性又有value属性 * 2.存在ref属性或者value属性且又有子元素 */ this.error(elementName + " is only allowed to contain either \'ref\' attribute OR \'value\' attribute OR sub-element", ele); } if(var11) { //ref属性的处理,使用RuntimeBeanReference封装对应的ref名称 String var13 = ele.getAttribute("ref"); if(!StringUtils.hasText(var13)) { this.error(elementName + " contains empty \'ref\' attribute", ele); } RuntimeBeanReference ref = new RuntimeBeanReference(var13); ref.setSource(this.extractSource(ele)); return ref; } else if(var12) { //value属性的处理,使用TypedStringValue封装 TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute("value")); valueHolder.setSource(this.extractSource(ele)); return valueHolder; } else if(subElement != null) { //解析子元素 return this.parsePropertySubElement(subElement, bd); } else { //既没有ref也没有value也没有子元素 this.error(elementName + " must specify a ref or value", ele); return null; }}
从代码上来看,对构造函数中属性元素的解析,经历了以下几个过程
(1)略过description或meta
(2)提取constructor-arg上的ref和value属性,以便于根据规则验证正确性,其规则为在constructor-arg上不存在以下情况
同时既有ref属性又有value属性
存在ref属性或者value属性且又有子元素
(3)ref属性的处理。使用RuntimeBeanReference封装对应的ref名称,如:
<constructor-arg ref="a">
(4)value属性的处理,使用TypedStringValue封装,例如:
<constructor-arg value="a">
(5)子元素的处理,例如:
<constructor-arg> <map> <entry key="key" value="value" /> </map></constructor-arg>
parsePropertySubElement中实现了对各种子元素的分类处理
public Object parsePropertySubElement(Element ele, BeanDefinition bd) { return this.parsePropertySubElement(ele, bd, (String)null);}public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { if(!this.isDefaultNamespace(this.getNamespaceURI(ele))) { return this.parseNestedCustomElement(ele, bd); } else if(this.nodeNameEquals(ele, "bean")) { BeanDefinitionHolder nullHolder2 = this.parseBeanDefinitionElement(ele, bd); if(nullHolder2 != null) { nullHolder2 = this.decorateBeanDefinitionIfRequired(ele, nullHolder2, bd); } return nullHolder2; } else if(this.nodeNameEquals(ele, "ref")) { String nullHolder1 = ele.getAttribute("bean"); boolean toParent = false; if(!StringUtils.hasLength(nullHolder1)) { //解析local nullHolder1 = ele.getAttribute("local"); if(!StringUtils.hasLength(nullHolder1)) { //解析parent nullHolder1 = ele.getAttribute("parent"); toParent = true; if(!StringUtils.hasLength(nullHolder1)) { this.error("\'bean\', \'local\' or \'parent\' is required for <ref> element", ele); return null; } } } if(!StringUtils.hasText(nullHolder1)) { this.error("<ref> element contains empty target attribute", ele); return null; } else { RuntimeBeanReference ref = new RuntimeBeanReference(nullHolder1, toParent); ref.setSource(this.extractSource(ele)); return ref; } //对idref元素的解析 } else if(this.nodeNameEquals(ele, "idref")) { return this.parseIdRefElement(ele); //对value元素的解析 } else if(this.nodeNameEquals(ele, "value")) { return this.parseValueElement(ele, defaultValueType); //对null元素的解析 } else if(this.nodeNameEquals(ele, "null")) { TypedStringValue nullHolder = new TypedStringValue((String)null); nullHolder.setSource(this.extractSource(ele)); return nullHolder; } else if(this.nodeNameEquals(ele, "array")) { //解析array子元素 return this.parseArrayElement(ele, bd); } else if(this.nodeNameEquals(ele, "list")) { //解析list子元素 return this.parseListElement(ele, bd); } else if(this.nodeNameEquals(ele, "set")) { //解析set子元素 return this.parseSetElement(ele, bd); } else if(this.nodeNameEquals(ele, "map")) { //解析map子元素 return this.parseMapElement(ele, bd); } else if(this.nodeNameEquals(ele, "props")) { //解析props子元素 return this.parsePropsElement(ele); } else { this.error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; }}
7.解析子元素property
parsePropertyElement函数完成了对property属性的提取,property使用方式如下:
<bean id="test" class="test.TestClass"> <property name="testStr" value="aaa" /></bean>或者<bean id="a"> <property name="p"> <list> <value>aa</value> <value>bb</value> </list> </property></bean>
而具体的解析过程如下:
public void parsePropertyElements(Element beanEle, BeanDefinition bd) { NodeList nl = beanEle.getChildNodes(); for(int i = 0; i < nl.getLength(); ++i) { Node node = nl.item(i); if(node instanceof Element && this.nodeNameEquals(node, "property")) { this.parsePropertyElement((Element)node, bd); } }}public void parsePropertyElement(Element ele, BeanDefinition bd) { //获取配置元素中name的值 String propertyName = ele.getAttribute("name"); if(!StringUtils.hasLength(propertyName)) { this.error("Tag \'property\' must have a \'name\' attribute", ele); } else { this.parseState.push(new PropertyEntry(propertyName)); try { //不允许多次对同一属性配置 if(bd.getPropertyValues().contains(propertyName)) { this.error("Multiple \'property\' definitions for property \'" + propertyName + "\'", ele); return; } Object val = this.parsePropertyValue(ele, bd, propertyName); PropertyValue pv = new PropertyValue(propertyName, val); this.parseMetaElements(ele, pv); pv.setSource(this.extractSource(ele)); bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } }}
上面函数与构造函数注入方式不同的是将返回值使用PropertyValue进行封装,并记录在了BeanDefinition中的propertyValues属性中
8.解析子元素qualifier
对于qualifier元素的获取,接触更多的是注解的形式,在使用Spring框架中进行自动注入时,Spring容器中匹配的候选Bean数目必须有且仅有一个,当找不到一个匹配的Bean时,Spring容器将抛出BeanCreationException异常,并指出必须至少拥有一个匹配的Bean
Spring允许我们通过Qualifier指定注入Bean的名称,这样歧义就消除了,而对于配置方式使用如下:
<bean id="myTestBean" class="bean.MyTestBean"> <qualifier type="org.Springframework.beans.factory.annotation.Qualifier" value="qf" /></bean>
AbstractBeanDefinition属性
XML中所有的配置都可以在GenericBeanDefinition的实例类中找到对应的配置
GenericBeanDefinition只是子类实现,而大部分的通用属性都保存在了AbstractBeanDefinition中
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable { ... //省略静态变量以及final常量 //bean的作用范围,对应bean属性scope private String scope; //是否是单例,来自bean属性scope private boolean singleton; //是否是原型,来自bean属性scope private boolean prototype; //是否是抽象,对应bean属性abstract private boolean abstractFlag; //是否延迟加载,对应bean属性lazy-init private boolean lazyInit; //自动注入模式,对应bean属性autowire private int autowireMode; //依赖检查 private int dependencyCheck; //用来表示一个bean的实例化依靠另一个bean先实例化,对应bean属性depend-on private String[] dependsOn; //autowire-candidate属性设为false,这样容器在查找自动装配对象时,将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者,但是该bean本身还是可以使用自动装配来注入其他bean的 private boolean autowireCandidate; //自动装配时当出现多个bean候选者时,将作为首选者,对应bean属性primary private boolean primary; //用于记录Qualifier,对应子元素qualifier private final Map<String, AutowireCandidateQualifier> qualifiers; //允许访问非公开的构造器和方法,程序设置 private boolean nonPublicAccessAllowed; /** *是否以一种宽松的模式解析构造函数,默认为true,如果为false,则在如下情况: *interface ITest{} *class ITestImpl implements ITest{}; *class Main{ * Main(ITest i){} * Main(ITestImpl i){} *} *抛出异常,因为Spring无法精确定位哪个构造函数 */ private boolean lenientConstructorResolution; //记录构造函数注入属性,对应bean属性constructor-arg private ConstructorArgumentValues constructorArgumentValues; //普通属性集合 private MutablePropertyValues propertyValues; //方法重写的持有者,记录lookup-method、replaced-method元素 private MethodOverrides methodOverrides; //对应bean属性factory-bean private String factoryBeanName; //对应bean属性factory-method private String factoryMethodName; //初始化方法,对应bean属性init-method private String initMethodName; //销毁方法,对应bean属性destory-method private String destroyMethodName; //是否执行init-method,程序设置 private boolean enforceInitMethod; //是否执行destroy-method,程序设置 private boolean enforceDestroyMethod; //是否是用户定义的而不是应用程序本身定义的,创建AOP时候为true,程序设置 private boolean synthetic; //定义这个bean的应用,APPLICATION:用户,INFRASTRUCTURE:完全内部使用,与用户无关,SUPPORT:某些复制配置的一部分,程序设置 private int role; //bean的描述信息 private String description; //这个bean定义的资源 private Resource resource; //此处省略set/get方法}
解析默认标签中的自定义标签元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if(bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException var5) { this.getReaderContext().error("Failed to register bean definition with name \'" + bdHolder.getBeanName() + "\'", ele, var5); } this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); }}
以上已经用了大量篇幅分析了BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)这句代码,接下来要分析bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder)代码的分析
当Spring中的bean使用的是默认标签配置,但是其中的子元素却使用了自定义的配置时,这句代码就会起作用,如下场景:
<bean id="test" class="test.MyClass"> <mybean:user username="aaa"/></bean>
跟踪代码
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) { return this.decorateBeanDefinitionIfRequired(ele, definitionHolder, (BeanDefinition)null);}
这里第三个参数是父类bean,当对某个嵌套配置进行分析时,这里需要传递父类BeanDefinition,分析源码得知这里传递的参数其实是为了使用父类的scope属性,以备子类若没有设置scope时默认使用父类的属性,这里分析的是顶层配置,所以传递null。将第三个参数设置为空后进一步跟踪函数:
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) { BeanDefinitionHolder finalDefinition = definitionHolder; NamedNodeMap attributes = ele.getAttributes(); //遍历所有的属性,看看是否有适用于修饰的属性 for(int children = 0; children < attributes.getLength(); ++children) { Node i = attributes.item(children); finalDefinition = this.decorateIfRequired(i, finalDefinition, containingBd); } NodeList var9 = ele.getChildNodes(); //遍历所有的子节点,看看是否有适用于修饰的子元素 for(int var10 = 0; var10 < var9.getLength(); ++var10) { Node node = var9.item(var10); if(node.getNodeType() == 1) { finalDefinition = this.decorateIfRequired(node, finalDefinition, containingBd); } } return finalDefinition;}
decorateIfRequired函数代码如下:
private BeanDefinitionHolder decorateIfRequired(Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) { //获取自定义标签的命名空间 String namespaceUri = this.getNamespaceURI(node); //对于非默认标签进行修饰 if(!this.isDefaultNamespace(namespaceUri)) { //根据命名空间找到对应的处理器 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if(handler != null) { //进行修饰 return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd)); } if(namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) { this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node); } else if(this.logger.isDebugEnabled()) { this.logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]"); } } return originalDef;}public String getNamespaceURI(Node node) { return node.getNamespaceURI();}public boolean isDefaultNamespace(String namespaceUri) { return !StringUtils.hasLength(namespaceUri) || "http://www.springframework.org/schema/beans".equals(namespaceUri);}
程序首先获取属性或者元素的命名空间,以此来判断该元素或者属性是否适用于自定义标签的解析条件,找出自定义类型所对应的NamespaceHandler并进行进一步解析
总结下decorateBeanDefinitionIfRequired方法的作用,在decorateBeanDefinitionIfRequired中对于程序默认的标签的处理其实是直接略过的,因为默认的标签到这里已经被处理完了,这里只对自定义的标签或者说对bean的自定义属性感兴趣。在方法中实现了寻找自定义标签并根据自定义标签寻找命名空间处理器,并进行进一步的解析
注册解析的BeanDefinition
在对配置文件解析完、装饰完后,接下来就是进行注册了
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { //使用beanName做唯一标识注册 String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); //注册所有的别名 String[] aliases = definitionHolder.getAliases(); if(aliases != null) { String[] var7 = aliases; int var6 = aliases.length; for(int var5 = 0; var5 < var6; ++var5) { String aliase = var7[var5]; registry.registerAlias(beanName, aliase); } }}
从上面的代码可以看出,解析的BeanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,而对于BeanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册
1.通过beanName注册BeanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if(beanDefinition instanceof AbstractBeanDefinition) { try { /** *注册前的最后一次校验,这里的校验不同于之前的XML文件校验, *主要是对于AbstractBeanDefinition属性中的methodOverrides校验, *校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在 */ ((AbstractBeanDefinition)beanDefinition).validate(); } catch (BeanDefinitionValidationException var6) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var6); } } Map ex = this.beanDefinitionMap; //因为BeanDefinitionMap是全局变量,这里定会存在并发访问的情况 synchronized(this.beanDefinitionMap) { Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); //处理注册已经注册的beanName情况 if(oldBeanDefinition != null) { //如果对应的BeanName已经注册且在配置中配置了bean不允许被覆盖,则抛出异异常 if(!this.allowBeanDefinitionOverriding) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean \'" + beanName + "\': There is already [" + oldBeanDefinition + "] bound."); } if(this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean \'" + beanName + "\': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { //记录beanName this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; } //注册BeanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); //重置所有beanName对应的缓存 this.resetBeanDefinition(beanName); }}
上面代码中,在对于bean的注册处理方式上,主要进行了几个步骤
- 对AbstractBeanDefinition的校验,此时的校验是针对AbstractBeanDefinition的methodOverrides属性的
- 对beanName已经注册的情况的处理,如果设置了不允许bean的覆盖,则需要抛出异常,否则直接覆盖
- 加入map缓存
- 清除解析之前留下的对应beanName的缓存
2.通过别名注册BeanDefinition
public void registerAlias(String name, String alias) { Assert.hasText(name, "\'name\' must not be empty"); Assert.hasText(alias, "\'alias\' must not be empty"); //如果beanName与alias相同的话不记录alias,并删除对应的alias if(alias.equals(name)) { this.aliasMap.remove(alias); } else { //如果alias不允许被覆盖则抛出异常 if(!this.allowAliasOverriding()) { String registeredName = (String)this.aliasMap.get(alias); if(registeredName != null && !registeredName.equals(name)) { throw new IllegalStateException("Cannot register alias \'" + alias + "\' for name \'" + name + "\': It is already registered for name \'" + registeredName + "\'."); } } this.aliasMap.put(alias, name); }}
由以上代码可以得知注册alias的步骤如下:
- alias与beanName相同情况处理,若alias与beanName名称相同则不需要处理并删除原有的alias
- alias覆盖处理,若aliasName已经使用并已经指向了另一beanName则需要用户的设置进行处理
- 注册alias
通知监听器解析及注册完成
通过代码getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)完成此工作,这里的实现只为扩展,当程序开发人员需要对注册BeanDefinition事件进行监听时可以通过注册监听器的方式并将处理逻辑写入监听器中
alias标签的解析
在对bean进行定义时,除了使用id属性来指定名称之外,为了提供多个名称,可以使用alias标签来指定。而所有的这些名称都指向同一个bean,在某些情况下提供别名非常有用, 比如为了让应用的每一个组件能更容易地对公共组件进行引用
然而,在定义bean时就指定所有的别名并不是总是恰当的,有时我们期望能在当前位置为那些在别处定义的bean引入别名,在XML配置文件中,可以用单独的<alias/>元素来完成bean别名的定义,如配置文件中定义了一个JavaBean:
<bean id="testBean" class="com.test" />
要给这个JavaBean增加别名,以方便不同对象来调用,可以直接使用bean标签中的name属性:
<bean id="testBean" name="testBean,testBean2" class="com.test" />
同样,Spring还有另外一种声明别名的方式:
<bean id="testBean" class="com.test" /><alias name="testBean" alias="testBean,testBean2" />
使用别名可以让每个组件及主程序通过唯一名字来引用同一个数据源而互不干扰
对于alias标签的解析过程如下:
protected void processAliasRegistration(Element ele) { //获取beanName String name = ele.getAttribute("name"); //获取alias String alias = ele.getAttribute("alias"); boolean valid = true; if(!StringUtils.hasText(name)) { this.getReaderContext().error("Name must not be empty", ele); valid = false; } if(!StringUtils.hasText(alias)) { this.getReaderContext().error("Alias must not be empty", ele); valid = false; } if(valid) { try { //注册alias this.getReaderContext().getRegistry().registerAlias(name, alias); } catch (Exception var6) { this.getReaderContext().error("Failed to register alias \'" + alias + "\' for bean with name \'" + name + "\'", ele, var6); } //别名注册后通知监听器做相应处理 this.getReaderContext().fireAliasRegistered(name, alias, this.extractSource(ele)); }}
import标签的解析
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN//EN" "http://www.Springframework.org/dtd/Spring-beans.dtd"> <import resource="customerContext.xml" /> <import resource="systemContext.xml" /> ...</beans>
applicationContext.xml文件中使用import的方式导入有模块配置文件,以后若有新模块的加入,可以简单修改这个文件,这样大大简化了配置后期维护的复杂度,并使配置模块化,易于管理
protected void importBeanDefinitionResource(Element ele) { //获取resource属性 String location = ele.getAttribute("resource"); //如果不存在resource属性则不做任何处理 if(!StringUtils.hasText(location)) { this.getReaderContext().error("Resource location must not be empty", ele); } else { //解析系统属性,格式如:"${user.dir}" location = SystemPropertyUtils.resolvePlaceholders(location); LinkedHashSet actualResources = new LinkedHashSet(4); boolean absoluteLocation = false; try { //判定location是绝对URI还是相对URI absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException var11) { ; } int actResArray; //如果是绝对URI则直接根据地址加载对应的配置文件 if(absoluteLocation) { try { actResArray = this.getReaderContext().getReader().loadBeanDefinitions(location, actualResources); if(this.logger.isDebugEnabled()) { this.logger.debug("Imported " + actResArray + " bean definitions from URL location [" + location + "]"); } } catch (BeanDefinitionStoreException var10) { this.getReaderContext().error("Failed to import bean definitions from URL location [" + location + "]", ele, var10); } } else { //如果是相对地址则根据相对地址计算出绝对地址 try { //Resource存在多个子实现类,如VfsResource、FileSystemResource等,而每个resource的createRelative方式实现都不一样,所以这里先使用子类的方法尝试解析 Resource relativeResource = this.getReaderContext().getResource().createRelative(location); if(relativeResource.exists()) { actResArray = this.getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } else { //如果解析不成功,则使用默认的解析器ResourcePatternResolver进行解析 String baseLocation = this.getReaderContext().getResource().getURL().toString(); actResArray = this.getReaderContext().getReader().loadBeanDefinitions(StringUtils.applyRelativePath(baseLocation, location), actualResources); } if(this.logger.isDebugEnabled()) { this.logger.debug("Imported " + actResArray + " bean definitions from relative location [" + location + "]"); } } catch (IOException var8) { this.getReaderContext().error("Failed to resolve current resource location", ele, var8); } catch (BeanDefinitionStoreException var9) { this.getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, var9); } } //解析后进行监听器激活处理 Resource[] actResArray1 = (Resource[])actualResources.toArray(new Resource[actualResources.size()]); this.getReaderContext().fireImportProcessed(location, actResArray1, this.extractSource(ele)); }}
在解析import标签时,Spring进行解析的步骤大致如下:
- 获取resource属性所表示的路径
- 解析路径中的系统属性,格式如”${user.dir}”
- 判定location是绝对路径还是相对路径
- 如果是绝对路径则递归调用bean的解析过程,进行另一次的解析
- 如果是相对路径则计算出绝对路径并进行解析
- 通知监听器,解析完成
嵌入式beans标签的解析
递归调用beans的解析过程
- 《Spring源码深度解析》学习笔记——默认标签的解析
- spring源码深度解析(笔记二)--默认标签的解析
- 《Spring源码深度解析》学习笔记——自定义标签的解析
- 《Spring源码深度解析》阅读笔记5-默认标签的解析之bean标签的解析及注册
- spring源码深度解析(笔记三)--自定义标签的解析
- Spring源码解析-默认标签的解析
- Spring学习(3)——默认标签的解析
- 《Spring源码深度解析》学习笔记——bean的加载(一)
- Spring源码解析之默认标签的解析(一)
- 《Spring源码深度解析》阅读笔记6-自定义标签的解析
- 《Spring源码深度解析》学习笔记——Spring的整体架构与容器的基本实现
- Spring笔记03-Spring解析默认标签
- Spring源码深度解析 带标签
- spring默认标签解析
- Spring源码解析-自定义标签的解析
- spring源码深度解析(笔记一)
- Spring源码解析之一 ------ 默认标签的解析注册(IOC的第一步)
- spring源码深度解析(笔记四)--bean的加载
- 利用Matlab开发基于XSENS Mtw传感器模块的行人室内实时定位系统的相关
- POJ 3173 Parkside's Triangle G++
- windows程序设计 初入门的小理解
- <有用>解决Apache长时间占用内存大的问题,Apache 内存优化方法-以及查看线程进程命令
- AD中设置元件间的间隔规则
- 《Spring源码深度解析》学习笔记——默认标签的解析
- Bitmap_图片硬盘缓存(3)
- 什么是「穷人思维」?
- PHP学习3
- 文章标题今日收获
- linux SSH免密码远程登录
- Linux下实现冒泡排序
- Java入门菜鸟 高手勿喷
- html学习第四天以及css初识