您的位置:首页 > 编程语言 > Java开发

spring源码(4)之解析默认命名空间

2015-12-09 17:36 806 查看
spring源码之解析默认命名空间 上篇博文讨论了spring解析配置文件applicationContext.xml的document对象的大致流程:大体分为默认命名空间和非默认命名空间的解析。默认命名空间为bean标签,alias标签和import标签,而非默认空间主要为aop标签 和管理事务的标签。
if (delegate.isDefaultNamespace(root.getNamespaceURI())) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
String namespaceUri = ele.getNamespaceURI();
//默认命名空间
if (delegate.isDefaultNamespace(namespaceUri)) {
parseDefaultElement(ele, delegate);
}
else {
//非默认命名空间
delegate.parseCustomElement(ele);
}
}
}
}
else {

delegate.parseCustomElement(root);
}
默认命名空间的解析
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//解析import标签
if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//解析alias标签
else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//解析bean标签
else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
}
非默认命名空间的解析
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = ele.getNamespaceURI();
//给相应的命名空间选择解析器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
//调用相应解析器的解析方法
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
从上面就可以对spring解析配置文件的过程一目了然了。以后着重探讨每个解析的细节。

一、解析bean标签
从上面可以看出,解析bean标签,调用的processBeanDefinition()方法
//【代码清单】: BeanDefinitionParserDelegate解析bean
protectedvoid processBeanDefinition(Elementele, BeanDefinitionParserDelegate delegate) {
//1.委托给BeanDefinitionParserDelegate来完成对bean元素的处理,这个类包含了具体的bean解析过程,解析后把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要载体,也是ioc容器的管理对象;然后把beanDefinition交给BeanDefinitonHold管理
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//2.用于扩展BeanDefinition
bdHolder= delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//3.向ioc容器注册,实际上是放到放到ioc容器DefaultListableBeanFactory的一个map里
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());
}
// 4.向IOC容器发送时间,表示解析和注册完成
getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder));
}
}
从上面可以看到,解析bean标签,其实就是把bean标签里面的配置信息,封装到beanDefinition对象,然后把这个对象放到容器application的一个map中。我们先来看看它是怎么放进map中,再来看它是如何封装的。

(1)把封装对象放进容器
我们来看看BeanDefinitionReaderUtils的registerBeanDefinition()方法
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {

// Register bean definition under primary name.
//获得beanDefinition对象的名字
String beanName = definitionHolder.getBeanName();
//把对象放进容器
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (int i = 0; i < aliases.length; i++) {
registry.registerAlias(beanName, aliases[i]);
}
}
}
这个方法传进2个参数,一个是容器(registy),一个是要放进容器的对象(beanDefinition),这个BeanDefinitionRegistry是一个接口,前面实例化的容器DefaultListableBeanFactory是它 的一个实现类。这里调用DefaultListableBeanFactory.registerBeanDefinition()方法,把对象放进容器。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {

if (beanDefinition instanceof AbstractBeanDefinition) {
try {
//校验
((AbstractBeanDefinition) beanDefinition).validate();
}
}
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {

}
else {
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
//把beanDefinition以beanName为key,beanDefinition为value放进一个map中
this.beanDefinitionMap.put(beanName, beanDefinition);

resetBeanDefinition(beanName);
}
}
所以,我们平时说把一个对象放进容器里面,其实就是把这个对象放进容器对象BeanFactory的一个map中,下面我们就来看看spring是如何把xml中的配置封装成对象的。

(2)把配置信息封装成对象
bean标签里面有什么属性呢?我们先来看bean标签
<bean id=""
name=""
<!--使用Class属性指定类的默认构造方法创建一个单例Bean,名称由id属性指定-->
class=""
<!--作用域,默认为singleTon -->
scope=""
<!-- 初始化时调用的方法 -->
init-method=""
<!-- 对象在销毁时要调用的方法 -->
destroy-method=""
<!-- 要求Spring采用何种方式来检查bean的setting属性设置情况 -->
dependency-check="default"
<!-- 该bean初始化之前,要求Spring容器事先将所依赖的bean也实例化 -->
depends-on=""
<!-- 指定一个工厂方法来创建此bean -->
factory-bean=""
<!-- 对象在销毁时要调用的方法 -->
factory-method=""
<!-- 如果设置为true,那么Spring容器认定此bean为抽象的,并且不会对它进行初始化 -->
abstract="true"
<!-- 如果设置为false,该bean不会被当作其它bean的autowire候选对象 -->
autowire-candidate="default"
<!-- 如果该bean需要autowire的话,声明采用哪一种autowire。可选的值有:byType,byname,constractor,autodetect或no -->
autowire="default"
<!-- 该bean被初始化或被注入到别的bean中时,Spring容器回调该方法 -->
lazy-init="default"
<!-- 指定bean在配置文件中的父类 -->
parent=""
<!-- 对象在销毁时要调用的方法 -->
primary="true">
</bean>
和struct2解析标签一样,一个标签有自身带的属性,还会有子标签。
<!-- property标签用于对bean实例中的属性进行赋值,对于基本数据类型的值可由value属性直接指定,而ref则表示对其他bean实例的引用-->
<property name="属性的名称" ref="用引用的bean的名称" value="属性值">
</property>
<!-- private String name; -->
<property name="属性的名称" value="属性值">

</property>
<!-- private BeanFactory factory; -->
<property name="">
<!创建一个内部匿名Bean实例赋值给指定的属性-->
<bean class=""></bean>
</property>
<!-- private List<BeanFactory>factory; -->
<property name="">
<!-- list标签用于创建一个list类型的实例赋值给指定的List类型属性,List实例中的元素通过value或ref子标签指定。对于基本数据类型的元素由value标签生成,如果需要引用其他bean实例作为set元素的话,可由ref标签指定 -->
<list>
<value></value>
<ref bean=""/>
</list>
</property>
<!-- private Map<BeanFactory>factory; -->
<property name="">
<!-- Map标签用于创建一个Map类型的实例赋值给指定的Map类型属性,Map实例中的元素通过entry标签的key属性指定,值则可通过value或ref子标签指定。对于基本数据类型的元素由value标签生成,如果需要引用其他bean实例的话,可由ref标签指定 -->
<map>
<entry key=""key-ref="" value=""value-ref=""></entry>
</map>
</property>

<!-- private Set<BeanFactory>factory; -->
<property name="">
<!-- set标签用于创建一个set类型的实例赋值给指定的set类型属性,set实例中的元素通过value或ref子标签指定。对于基本数据类型的元素由value标签生成,如果需要引用其他bean实例作为set元素的话,可由ref标签指定 -->
<set>
<value>
</value>
<ref bean=""local="" parent=""/>
</set>
</property>
<property name="">
<!-- 创建一个properties类型的实例赋给指定的properties类型属性 -->
<props>                                                                                        <prop key="properties元素的key">properties元素的value</prop>
</props>
</property>
<!--使用方法来代替getter注入。指定一个方法,它会在运行被复写从而返回一个指定的bean。这就是我们常说的getter注入 -->
<lookup-method bean="" name=""/>
<!-- 用一个新的实现来代替bean的某个方法 -->
<replaced-method name="" replacer="">
</replaced-method>
<!-- 想要让@Autowired方式按照name方式注入,可以结合@Qualifier("XX")使用,让@Autowired按照byName方式装配,消除歧义-->
<qualifier value=""type="org.springframework.beans.factory.annotation.Qualifier">
</qualifier>
<meta key=""value=""/>
<!-- 通过传入相应的构造参数进行bean实例化,constructor-arg标签用于指定一个构造参数,其index属性表明当前是第几个构成参数(从0开始),type属性声明构造参数的类型,构造参数的值如果是基本数据类型可由value属性直接指定,如果是对象的引用,则由ref属性指定 -->
<constructor-arg index="从0开始的序号" type="构造参数的类型" value="构造参数的值" ref="要引用bean的名称">
</constructor-arg>
<!--用来描述Spring context或每个bean元素。虽然它会被Spirng容器所忽略,但<description>元素可以通过工具生成属于你的Spring context文档-->
<description>
</description>
</bean>

封装xml信息到对象,那么,那个对象必然有每个对应属性来存储。因beanDefinition代码较多,这里就不列出来了。下面我们来看看spring是如何封装这些属性的。

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele,BeanDefinition containingBean) {
//得到bean的id和name <bean id=""name="" class="">
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List aliases = new ArrayList();
//如果配置了多个name值,把name分割放到一个list集合中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr,BEAN_NAME_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
//把id作为bean的名字标识
String beanName = id;
if (!StringUtils.hasText(beanName) &&!aliases.isEmpty()) {
//如果id 为null name都不为null,则以第一个值为bean的名字
beanName = (String) aliases.remove(0);
}
//以beanname检查是否有相同的bean
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
//解析后把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要载体,也是ioc容器的管理对象
AbstractBeanDefinition beanDefinition =parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//如果没有beanName,生成beanName
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(),true);
}
else {
//得到beanName和ClassName
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName =beanDefinition.getBeanClassName();
if (beanClassName != null &&beanName.startsWith(beanClassName)&& beanName.length() > beanClassName.length() &&                     !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName,aliasesArray);
}
return  null;
}

这里大体的流程就是,封装bean属性信息到beanDefinition,然后把beanDefintion进一步封装到BeanDefinitionHolder,这个BeanDefinitionHolder主要是管理BeanDefinition的,通过这个BeanDefinitionHolder就可以获取BeanDefinition信息

public class BeanDefinitionHolder implements BeanMetadataElement {

private final BeanDefinition beanDefinition;

private final String beanName;

private final String[] aliases;

}
那他是怎么封装bean信息到beanDefinition对象的呢?
//【代码清单】生成beanDefinition对象

public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinitioncontainingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
//获得class属性<bean id="" name="" class=""/>
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
//获得parent属性
try {
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//1.创建BeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className,parent);
//2.封装属性到BeanDefinition对象
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//设置Descript属性
bd.setDescription(DomUtils.getChildElementValueByTagName(ele,DESCRIPTION_ELEMENT));
//3.对bean元素(二级类目)进行解析,如<bean><property/><bean>中的<property/>
//<meta>
parseMetaElements(ele, bd);
//LookupOverrideSub
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//ReplacedMethodSub
parseReplacedMethodSubElements(ele,bd.getMethodOverrides());
//ConstructorArg
parseConstructorArgElements(ele, bd);
//解析bean的property属性
parsePropertyElements(ele, bd);
//qualifiy
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
finally {
this.parseState.pop();
}

return null;
}

这里先实例化一个beanDefinition对象,然后封装bean的属性,最后封装bean的子标签。

①实例化BeanDefinition对象

【代码清单】创建createBeanDefinition
publicstatic AbstractBeanDefinition createBeanDefinition(
String parentName, String className, ClassLoaderclassLoader) throws ClassNotFoundException {
//new一个beanDefinition
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className,classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}

②封装bean属性值到BeanDefinition对象中

//【代码清单】组合信息到BeanDefinition
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, StringbeanName,
BeanDefinition containingBean, AbstractBeanDefinition bd){
if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
// 获得scope属性,并设置scope属性
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
//spring 2.x作用域是用scope,有了scope就不用有singleton,否则报错
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Specifyeither 'scope' or 'singleton', not both", ele);
}
}
elseif (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// Spring 1.x"singleton" 属性,如果为真,则scope为singleton,否则为prototype
bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?BeanDefinition.SCOPE_SINGLETON: BeanDefinition.SCOPE_PROTOTYPE);
}
elseif (containingBean != null) {
// 如果内钳了一个bean,以里面的bean的作用域的scope为准
bd.setScope(containingBean.getScope());
}
//获得abstract属性,true or false
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {       bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
//以下其他属性通过set方法添加到BeanDefinition中。代码省略。
return bd;
}
封装属性很简单,无非就是用set()方法,把xml中相应属性的值,设置到beanDefinition对象上。 ③封装bean子元素属性到对象中 解析子标签的,重点讲解解析property。原理都是把property的各个属性封装到不通的对象中。如property中的bean封装到beanDefinitionHolder,ref属性封装到RuntimeBeanReference,vale属性封装到TypedStringValue,map封装到managedMap,list封装到managedList,set封装到managedSet.
//【代码清单】得到property标签
public void parsePropertyElements(ElementbeanEle, BeanDefinition bd) {
//遍历元素下的所有子节点
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) {
//在判断是property属性后,对其解析
parsePropertyElement((Element) node, bd);
}
}
}
这个property标签,记录的就是javaBean的属性值。
//【代码清单】获得property属性
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//得到name属性<property name="">
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
//name属性为空,返回
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//name属性相同,返回
if (bd.getPropertyValues().contains(propertyName)) {
return;
}
//解析,解析结果会封装到PropertyValue对象中,然后设置到BeanDefinition中去
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
property有什么属性呢?
<!-【代码清单】property属性>
<!---------------给数组注入值----------------------->
<property name=”属性” >
<list>
<value></value>
</list>
</property>
<!---------------给list集合注入值----------------------->
<property name=”属性” >
<list>
<ref bean=’bean对象名’/>
</list>
</property>
(1)解析value和ref
//【代码清单】解析property
public Object parsePropertyValue(Element ele, BeanDefinition bd, StringpropertyName) {
String elementName = (propertyName != null) ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg>element";
// 获得子元素 ref, value, list, etc.代码省略
NodeList nl = ele.getChildNodes();
//这里判断property的属性,是ref还是value,不允许同时是ref和value.部分代码省略
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);

//如果是ref,创建一个ref的数据对象RuntimeBeanReference,这个对象封装了ref的信息
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
//如果是value,创建一个value的数据对象TypedStringValue ,这个对象封装了value的信息
elseif (hasValueAttribute) {
TypedStringValuevalueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
//如果还有子元素,如map.list.set,触发对子元素的解析.
elseif (subElement != null) {
return parsePropertySubElement(subElement, bd);
}
}
(2)解析其他属性
//【代码清单】解析其他子元素
public Object parsePropertySubElement(Element ele,BeanDefinition bd, String defaultTypeClassName) {
if (!isDefaultNamespace(ele.getNamespaceURI())) {
return parseNestedCustomElement(ele, bd);
}
//解析各种子元素.此处代码省略
//这里是对property子元素的解析过程,Array、List、Set、Map、Prop等各种元素都会在这里进行解析,生成对应的数据对象,比如ManagedList、ManagedArray、ManagedSet等。这些Managed类是Spring对具体的BeanDefinition的数据封装
//解析list
elseif (DomUtils.nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//解析set
elseif (DomUtils.nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//解析map
elseif (DomUtils.nodeNameEquals(ele, MAP_ELEMENT)) {
returnparseMapElement(ele, bd);
}
//解析props
elseif (DomUtils.nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
returnnull;
}
}
①解析list
//【代码清单】解析list
public List parseListElement(Element collectionEle,BeanDefinition bd) {
String defaultTypeClassName = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
NodeList nl = collectionEle.getChildNodes();
//把list数据封装到ManagedList对象
ManagedList list= new ManagedList(nl.getLength());
list.setSource(extractSource(collectionEle));
list.setMergeEnabled(parseMergeAttribute(collectionEle));
//遍历所有的元素节点,并判断其类型是否为Element
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !DomUtils.nodeNameEquals(node,DESCRIPTION_ELEMENT)) {
//加入到ManagedList,同时触发对下一层子元素的解析过程,
//这是一个递归的调用
list.add(parsePropertySubElement((Element)node, bd, defaultTypeClassName));
}
}
return list;
}
这里就是bean标签大致解析过程,解析配置文件,无非就是把配置属性设置到相应的对象中,然后又把对应的对象设置到一个对象中,比如这里的,把bean属性设置到beanDefinition对象上,把ref属性封装到RuntimeBeanReference,vale属性封装到TypedStringValue,map封装到managedMap,list封装到managedList,set封装到managedSet.然后把这些对象设置到beanDefinition对象上。除了设置值,无其他实质性的操作。

二、解析alias标签
protected void processAliasRegistration(Element ele) {

String name = ele.getAttribute(NAME_ATTRIBUTE);
//获取别名
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
//判断是否有别名
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
//如果有别名
if (valid) {
try {
getReaderContext().getRegistry().registerAlias(name, alias);
}
}
//通知封装成功
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}
如果有别名,spring会如何封装呢?
public void registerAlias(String name, String alias) {

if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
if (!allowAliasOverriding()) {
String registeredName = (String) this.aliasMap.get(alias);
//如果有别名,以别名为key值,name为value值,保存到一个map中
this.aliasMap.put(alias, name);
}
}
从这里可以看出,封装alias标签,其实就是把别名和name放到一个map中,建立对应关系。

三、解析import标签
protected void importBeanDefinitionResource(Element ele) {
//获取import标签的resouce属性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
if (!StringUtils.hasText(location)) {

return;
}

location = SystemPropertyUtils.resolvePlaceholders(location);
if (ResourcePatternUtils.isUrl(location)) {
try {
Set actualResources = new LinkedHashSet(4);
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
Resource[] actResArray = (Resource[]) actualResources.toArray(new Resource[actualResources.size()]);
}
}
else {

try {
//得到resouce对象
Resource relativeResource = getReaderContext().getResource().createRelative(location);
//调用loadBeanDefinitions方法
int importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
}

}
}
从这里看出,解析import标签,其实也是解析document对象

四。总结
本篇博文介绍了spring如何解析xml配置中的默认命名空间:把bean标签属性封装到相应的对象,然后把这些对象设置到beanDefinition对象中,跟着又把beanDefinition对象设置到beanDefinitionHolder对象中,最后把beanDefinitionHolder保存到beanFactory的一个map中(把对象放进容器)。下篇博文将介绍解析非默认命名空间。因为解析配置文件,归根到底就是把属性封装到beanDefinition对象中,所以下篇博文将简要介绍几个常用的标签,如aop和transaction.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息