【死磕 Spring】----- IOC 之 注册 BeanDefinition
原文出自:
获取 document 对象后,会根据该对象和 resource 资源对象调用 registerbeandefinitions()
方法,开始注册 beandefinitions 之旅。如下:
public int registerbeandefinitions(document doc, resource resource) throws beandefinitionstoreexception { beandefinitiondocumentreader documentreader = createbeandefinitiondocumentreader(); int countbefore = getregistry().getbeandefinitioncount(); documentreader.registerbeandefinitions(doc, createreadercontext(resource)); return getregistry().getbeandefinitioncount() - countbefore; }
首先调用 createbeandefinitiondocumentreader()
方法实例化 beandefinitiondocumentreader 对象,然后获取统计前 beandefinition 的个数,最后调用 registerbeandefinitions()
注册 beandefinition。
实例化 beandefinitiondocumentreader 对象方法如下:
protected beandefinitiondocumentreader createbeandefinitiondocumentreader() { return beandefinitiondocumentreader.class.cast(beanutils.instantiateclass(this.documentreaderclass)); }
注册 beandefinition 的方法 registerbeandefinitions()
是在接口 beandefinitiondocumentreader 中定义,如下:
void registerbeandefinitions(document doc, xmlreadercontext readercontext) throws beandefinitionstoreexception;
从给定的 document 对象中解析定义的 beandefinition 并将他们注册到注册表中。方法接收两个参数,待解析的 document 对象,以及解析器的当前上下文,包括目标注册表和被解析的资源。其中 readercontext 是根据 resource 来创建的,如下:
public xmlreadercontext createreadercontext(resource resource) { return new xmlreadercontext(resource, this.problemreporter, this.eventlistener, this.sourceextractor, this, getnamespacehandlerresolver()); }
defaultbeandefinitiondocumentreader 对该方法提供了实现:
public void registerbeandefinitions(document doc, xmlreadercontext readercontext) { this.readercontext = readercontext; logger.debug("loading bean definitions"); element root = doc.getdocumentelement(); doregisterbeandefinitions(root); }
调用 doregisterbeandefinitions()
开启注册 beandefinition 之旅。
protected void doregisterbeandefinitions(element root) { beandefinitionparserdelegate parent = this.delegate; this.delegate = createdelegate(getreadercontext(), root, parent); if (this.delegate.isdefaultnamespace(root)) { // 处理 profile string profilespec = root.getattribute(profile_attribute); if (stringutils.hastext(profilespec)) { string[] specifiedprofiles = stringutils.tokenizetostringarray( profilespec, beandefinitionparserdelegate.multi_value_attribute_delimiters); if (!getreadercontext().getenvironment().acceptsprofiles(specifiedprofiles)) { if (logger.isinfoenabled()) { logger.info("skipped xml bean definition file due to specified profiles [" + profilespec + "] not matching: " + getreadercontext().getresource()); } return; } } } // 解析前处理 preprocessxml(root); // 解析 parsebeandefinitions(root, this.delegate); // 解析后处理 postprocessxml(root); this.delegate = parent; }
程序首先处理 profile属性,profile主要用于我们切换环境,比如切换开发、测试、生产环境,非常方便。然后调用 parsebeandefinitions()
进行解析动作,不过在该方法之前之后分别调用 preprocessxml()
和 postprocessxml()
方法来进行前、后处理,目前这两个方法都是空实现,交由子类来实现。
protected void preprocessxml(element root) { } protected void postprocessxml(element root) { }
parsebeandefinitions()
定义如下:
protected void parsebeandefinitions(element root, beandefinitionparserdelegate delegate) { if (delegate.isdefaultnamespace(root)) { 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; if (delegate.isdefaultnamespace(ele)) { parsedefaultelement(ele, delegate); } else { delegate.parsecustomelement(ele); } } } } else { delegate.parsecustomelement(root); } }
最终解析动作落地在两个方法处:parsedefaultelement(ele, delegate)
和 delegate.parsecustomelement(root)
。我们知道在 spring 有两种 bean 声明方式:
- 配置文件式声明:
<bean id="studentservice" class="org.springframework.core.studentservice"/>
- 自定义注解方式:
<tx:annotation-driven>
两种方式的读取和解析都存在较大的差异,所以采用不同的解析方法,如果根节点或者子节点采用默认命名空间的话,则调用 parsedefaultelement()
进行解析,否则调用 delegate.parsecustomelement()
方法进行自定义解析。
至此,doloadbeandefinitions()
中做的三件事情已经全部分析完毕,下面将对 bean 的解析过程做详细分析说明。