Spring中BeanFactory解析bean详解
在该文中来讲讲spring框架中beanfactory解析bean的过程,该文之前在小编原文中有发表过,先来看一个在spring中一个基本的bean定义与使用。
package bean; public class testbean { private string beanname = "beanname"; public string getbeanname() { return beanname; } public void setbeanname(string beanname) { this.beanname = beanname; } }
spring配置文件root.xml定义如下:
<?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-4.1.xsd"> <bean id="testbean" class="bean.testbean"> </beans>
下面使用xmlbeanfactory来获取该bean:
public class beantest { private static final java.util.logging.logger logger = loggerfactory.getlogger(beantest.class); @test public void getbeantest() { beanfactory factory = new xmlbeanfactory(new classpathresource("root.xml")); testbean bean = factory.getbean("testbean"); logger.info(bean.getbeanname); } }
这个单元测试运行结果就是输出beanname,上面就是spring最基本的bean的获取操作,这里我用beanfactory作为容器来获取bean的操作并不多见,在企业开发中一般是使用功能更完善的applicationcontext,这里先不讨论这个,下面重点讲解使用beanfactory获取bean的过程。
现在就来分析下上面的测试代码,看看spring到底为我们做了什么工作,上面代码完成功能的流程不外乎如此:
1. 读取spring配置文件root.xml;
2. 根据root.xml中的bean配置找到对应的类的配置,并实例化;
3. 调用实例化后的对象输出结果。
先来看看xmlbeanfactory源码:
public class xmlbeanfactory extends defaultlistablebeanfactory { private final xmlbeandefinitionreader reader = new xmlbeandefinitionreader(this); public xmlbeanfactory(resource resource) throws beansexception { this(resource, null); } public xmlbeanfactory(resource resource, beanfactory parentbeanfactory) throws beansexception { super(parentbeanfactory); this.reader.loadbeandefinitions(resource); } }
从上面可以看出xmlbeanfactory继承了defaultlistablebeanfactory,defaultlistablebeanfactory是spring注册加载bean的默认实现,它是整个bean加载的核心部分,xmlbeanfactory与它的不同点就是xmlbeanfactory使用了自定义的xml读取器xmlbeandefinitionreader,实现了自己的beandefinitionreader读取。
xmlbeanfactory加载bean的关键就在于xmlbeandefinitionreader,下面看看xmlbeandefinitionreader的源码(只列出部分):
public class xmlbeandefinitionreader extends abstractbeandefinitionreader { private class<?> documentreaderclass = defaultbeandefinitiondocumentreader.class; private problemreporter problemreporter = new failfastproblemreporter(); private readereventlistener eventlistener = new emptyreadereventlistener(); private sourceextractor sourceextractor = new nullsourceextractor(); private namespacehandlerresolver namespacehandlerresolver; private documentloader documentloader = new defaultdocumentloader(); private entityresolver entityresolver; private errorhandler errorhandler = new simplesaxerrorhandler(logger); }
xmlbeandefinitionreader继承自abstractbeandefinitionreader,下面是abstractbeandefinitionreader的源码(只列出部分):
public abstract class abstractbeandefinitionreader implements environmentcapable, beandefinitionreader { protected final log logger = logfactory.getlog(getclass()); private final beandefinitionregistry registry; private resourceloader resourceloader; private classloader beanclassloader; private environment environment; private beannamegenerator beannamegenerator = new defaultbeannamegenerator(); }
xmlbeandefinitionreader主要通过以下三步来加载spring配置文件中的bean:
1. 通过继承自abstractbeandefinitionreader中的方法,使用resourloader将资源文件(root.xml)路径转换为对应的resource文件;
2. 通过documentloader对resource文件进行转换,将resource文件转换为ducument文件;
3. 通过defaultbeandefinitiondocumentreader类对document进行解析,最后再对解析后的element进行解析。
了解以上基础后,接下来详细分析下一开始例子中的代码:
beanfactory factory = new xmlbeanfactory(new classpathresource("root.xml"));
先看看下面xmlbeanfactory初始化的时序图来进一步了解这段代码的执行,
在这里可以看出beantest测试类通过向classpathresource的构造方法传入spring的配置文件构造一个resource资源文件的实例对象,再通过这个resource资源文件来构造我们想要的xmlbeanfactory实例。在前面xmlbeanfactory源码中的构造方法可以看出,
public xmlbeanfactory(resource resource) throws beansexception { this(resource, null); } public xmlbeanfactory(resource resource, beanfactory parentbeanfactory) throws beansexception { super(parentbeanfactory); this.reader.loadbeandefinitions(resource); }
this.reader.loadbeandefinition(resource)就是资源加载真正的实现,时序图中xmlbeandefinitionreader加载数据就是在这里完成的。
接下来跟进this.reader.loadbeandefinition(resource)方法里面,
public class xmlbeandefinitionreader extends abstractbeandefinitionreader { @override public int loadbeandefinitions(resource resource) throws beandefinitionstoreexception { return loadbeandefinitions(new encodedresource(resource)); } }
在loadbeandefinition(resource)方法里对资源文件resource使用encodedresource进行编码处理后继续传入loadbeandefinitions方法,继续跟进loadbeandefinitions(new encodedresource(resource))方法源码:
public int loadbeandefinitions(encodedresource encodedresource) throws beandefinitionstoreexception { assert.notnull(encodedresource, "encodedresource must not be null"); if (logger.isinfoenabled()) { logger.info("loading xml bean definitions from " + encodedresource.getresource()); } // 通过属性记录已加载的资源 set<encodedresource> currentresources = this.resourcescurrentlybeingloaded.get(); if (currentresources == null) { currentresources = new hashset<encodedresource>(4); this.resourcescurrentlybeingloaded.set(currentresources); } if (!currentresources.add(encodedresource)) { throw new beandefinitionstoreexception( "detected cyclic loading of " + encodedresource + " - check your import definitions!"); } try { // 从resource中获取对应的inputstream,用于下面构造inputsource inputstream inputstream = encodedresource.getresource().getinputstream(); try { inputsource inputsource = new inputsource(inputstream); if (encodedresource.getencoding() != null) { inputsource.setencoding(encodedresource.getencoding()); } // 调用doloadbeandefinitions方法 return doloadbeandefinitions(inputsource, encodedresource.getresource()); } finally { inputstream.close(); } } catch (ioexception ex) { throw new beandefinitionstoreexception( "ioexception parsing xml document from " + encodedresource.getresource(), ex); } finally { currentresources.remove(encodedresource); if (currentresources.isempty()) { this.resourcescurrentlybeingloaded.remove(); } } }
继续跟进doloadbeandefinitions(inputsource, encodedresource.getresource())方法,这是整个bean加载过程的核心方法,在这个方法执行bean的加载。
protected int doloadbeandefinitions(inputsource inputsource, resource resource) throws beandefinitionstoreexception { try { document doc = doloaddocument(inputsource, resource); return registerbeandefinitions(doc, resource); } /* 省略一堆catch */ }
跟进doloaddocument(inputsource, resource)源码:
protected document doloaddocument(inputsource inputsource, resource resource) throws exception { return this.documentloader.loaddocument(inputsource, getentityresolver(), this.errorhandler, getvalidationmodeforresource(resource), isnamespaceaware()); }
在doloaddocument(inputsource, resource)方法里就使用到了前面讲的documentloader加载document,这里documentloader是个接口,真正调用的是其实现类defaultdocumentloader的loaddocument方法,跟进源码:
public class defaultdocumentloader implements documentloader { @override public document loaddocument(inputsource inputsource, entityresolver entityresolver, errorhandler errorhandler, int validationmode, boolean namespaceaware) throws exception { documentbuilderfactory factory = createdocumentbuilderfactory(validationmode, namespaceaware); if (logger.isdebugenabled()) { logger.debug("using jaxp provider [" + factory.getclass().getname() + "]"); } documentbuilder builder = createdocumentbuilder(factory, entityresolver, errorhandler); return builder.parse(inputsource); } }
从源码可以看出这里先创建documentbuilderfactory,再用它创建documentbuilder,进而解析inputsource来返回document对象。得到document对象后就可以准备注册我们的bean信息了。
在上面的doloadbeandefinitions(inputsource, encodedresource.getresource())方法中拿到document对象后下面就是执行registerbeandefinitions(doc, resource)方法了,看源码:
public int registerbeandefinitions(document doc, resource resource) throws beandefinitionstoreexception { beandefinitiondocumentreader documentreader = createbeandefinitiondocumentreader(); documentreader.setenvironment(getenvironment()); // 还没注册bean前的beandefinition加载个数 int countbefore = getregistry().getbeandefinitioncount(); // 加载注册bean documentreader.registerbeandefinitions(doc, createreadercontext(resource)); // 本次加载注册的beandefinition个数 return getregistry().getbeandefinitioncount() - countbefore; }
这里的doc就是上面的loaddocument方法加载转换来的,从上面可以看出主要工作是交给beandefinitiondocumentreader的registerbeandefinitions()方法实现的,这里beandefinitiondocumentreader是个接口,注册bean功能在默认实现类defaultbeandefinitiondocumentreader的该方法实现,跟进它的源码:
@override public void registerbeandefinitions(document doc, xmlreadercontext readercontext) { this.readercontext = readercontext; logger.debug("loading bean definitions"); element root = doc.getdocumentelement(); doregisterbeandefinitions(root); }
到这里通过doc.getdocumentelement()获得element对象后,交给doregisterbeandefinitions()方法后就是真正执行xml文档的解析了,跟进doregisterbeandefinitions()方法源码:
protected void doregisterbeandefinitions(element root) { beandefinitionparserdelegate parent = this.delegate; this.delegate = createdelegate(getreadercontext(), root, parent); if (this.delegate.isdefaultnamespace(root)) { 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)) { return; } } } preprocessxml(root); parsebeandefinitions(root, this.delegate); postprocessxml(root); this.delegate = parent; }
到这里处理流程就很清晰了,先是对profile进行处理,之后就通过parsebeandefinitions()方法进行文档的解析操作,跟进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; // 下面对bean进行处理 if (delegate.isdefaultnamespace(ele)) { parsedefaultelement(ele, delegate); } else { delegate.parsecustomelement(ele); } } } } else { delegate.parsecustomelement(root); } }
上面if-else语句块中的parsedefaultelement(ele, delegate)和delegate.parsecustomelement(ele)就是对spring配置文件中的默认命名空间和自定义命名空间进行解析用的。在spring的xml配置中,默认bean声明就如前面定义的:
<bean id="testbean" class="bean.testbean">
自定义的bean声明如:
<tx:annotation-driven />
xmlbeanfactory加载bean的整个过程基本就讲解到这里了。希望对大家的学习有所帮助,也希望大家多多支持。