上节讲到了如何获取document,当把文件转换为document后,接下来的提取及注册bean就是我们的重头戏。
protected int doloadbeandefinitions(inputsource inputsource, resource resource) throws beandefinitionstoreexception { try { //从资源文件转换为document对象 document doc = doloaddocument(inputsource, resource); //解析document,并注册beandefiniton到工厂中 int count = registerbeandefinitions(doc, resource); if (logger.isdebugenabled()) { logger.debug("loaded " + count + " bean definitions from " + resource); } return count; } catch (beandefinitionstoreexception ex) { throw ex; } catch (saxparseexception ex) { throw new xmlbeandefinitionstoreexception(resource.getdescription(), "line " + ex.getlinenumber() + " in xml document from " + resource + " is invalid", ex); } catch (saxexception ex) { throw new xmlbeandefinitionstoreexception(resource.getdescription(), "xml document from " + resource + " is invalid", ex); } catch (parserconfigurationexception ex) { throw new beandefinitionstoreexception(resource.getdescription(), "parser configuration exception parsing xml from " + resource, ex); } catch (ioexception ex) { throw new beandefinitionstoreexception(resource.getdescription(), "ioexception parsing xml document from " + resource, ex); } catch (throwable ex) { throw new beandefinitionstoreexception(resource.getdescription(), "unexpected exception parsing xml document from " + resource, ex); } }
在xmlbeandefinitionreader的doloadbeandefinitions方法中,将document对象交给registerbeandefinitions方法,返回本次加载的beandefinition个数。
public int registerbeandefinitions(document doc, resource resource) throws beandefinitionstoreexception { //创建默认的documentreader,即defaultbeandefinitiondocumentreader,用来解析document beandefinitiondocumentreader documentreader = createbeandefinitiondocumentreader(); //获取注册中心,记录统计前的beandefinition加载个数 int countbefore = getregistry().getbeandefinitioncount(); //加载及注册beandefinition documentreader.registerbeandefinitions(doc, createreadercontext(resource)); //记录本次加载的beandefinition个数 return getregistry().getbeandefinitioncount() - countbefore; }
这里首先会createreadercontext(resource),代码很简单。
public xmlreadercontext createreadercontext(resource resource) { return new xmlreadercontext(resource, this.problemreporter, this.eventlistener, this.sourceextractor, this, getnamespacehandlerresolver()); }
这里重点是将this对象,也就是当前的xmlbeandefinitionreader对象放进去了,所以xmlreadercontext相当于一个上下文,方便数据的传递,类似于servletcontext,securitycontext等。
然后将document和上下文对象交给documentreader.registerbeandefinitions方法。
public void registerbeandefinitions(document doc, xmlreadercontext readercontext) { this.readercontext = readercontext; doregisterbeandefinitions(doc.getdocumentelement()); }
此处引用了之前的上下文,然后调用doregisterbeandefinitions,在spring中,很多以"do"开头的方法名就是真正干活的方法。
这里获取了document的根元素,进入documentreader.doregisterbeandefinitions方法
protected void doregisterbeandefinitions(element root) { // any nested <beans> elements will cause recursion in this method. in // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. beandefinitionparserdelegate parent = this.delegate; //委托给delegate解析 this.delegate = createdelegate(getreadercontext(), root, parent); //判断当前beans节点是否是默认命名空间 if (this.delegate.isdefaultnamespace(root)) { //获取beans节点的profile属性 string profilespec = root.getattribute(profile_attribute); if (stringutils.hastext(profilespec)) { //可以使用逗号或分号将当前beans标签指定为多个profile类型 string[] specifiedprofiles = stringutils.tokenizetostringarray( profilespec, beandefinitionparserdelegate.multi_value_attribute_delimiters); // we cannot use profiles.of(...) since profile expressions are not supported // in xml config. see spr-12458 for details. //判断当前beans标签的profile是否被激活 if (!getreadercontext().getenvironment().acceptsprofiles(specifiedprofiles)) { if (logger.isdebugenabled()) { logger.debug("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;
可以先根据我的注释看看这个方法到底是在干啥,此处documentreader对象引用了一个beandefinitionparserdelegate,又将解析过程委托给了delegate 处理,在parsebeandefinitions(root, this.delegate)方法中实现。
方法一开头,便有一大段英文注释,大致意思就是说,任何内嵌的beans标签,将会导致该方法的递归调用,为了正确的传播和保留<beans>标签的default属性,追踪当前delegate(即父delegete,可能为null),每次都会创建一个新的delegate(即子delegate),引用父delegate,而这个子delegate下次又会作为父delegate。
脑瓜子是不是有点嗡嗡的???但是大概也能明白这里就是为了处理beans标签的default属性的。
因为我们在配置xml文件的时候,是可以在根beans标签中嵌套beans标签的(虽然这样的写法很少,一般是写在另外一个xml文件中,然后通过import标签导入,但实际上效果是一样的),类似这样:
<?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" default-autowire="bytype"> <beans profile="test" default-autowire="bytype"> <bean id="user" class="cn.zxh.po.user" > <property name="name" value="张三"/> </bean> </beans> <beans profile="dev" default-autowire="constructor"> <bean id="user" class="cn.zxh.po.user"> <property name="name" value="李四"/> </bean> </beans> </beans>
理论上没有栈溢出的情况下beans内部应该是可以无限嵌套beans的(不一定正确,还没有试过),后面会讲到每次解析到beans标签都会进入到该方法中,所以该方法可能会递归调用,每次都会创建delegate,对应一个beans标签,根据父delegate来决定当前delegate的默认属性。
beandefinitionparserdelegate parent = this.delegate; this.delegate = createdelegate(getreadercontext(), root, parent);
createdelegate点进入看看。
protected beandefinitionparserdelegate createdelegate( xmlreadercontext readercontext, element root, @nullable beandefinitionparserdelegate parentdelegate) { beandefinitionparserdelegate delegate = new beandefinitionparserdelegate(readercontext); //根据父delegate的默认属性,初始化当前beans的默认属性 delegate.initdefaults(root, parentdelegate); return delegate; }
首先会实例化一个beandefinitionparserdelegate对象,该对象引用了之前的上下文readercontext,并且还引用了一个documentdefaultsdefinition。
private final documentdefaultsdefinition defaults = new documentdefaultsdefinition(); public beandefinitionparserdelegate(xmlreadercontext readercontext) { assert.notnull(readercontext, "xmlreadercontext must not be null"); this.readercontext = readercontext; }
documentdefaultsdefinition保存了根节点的默认配置属性值,比如说default-lazyinit,default-autowire,default-initmethod,default-destroymethod等,这几种属性应该都用过吧,如果该beans标签下的bean没有配置这些属性,就会使用beans标签的默认配置。
所以在这里,delegate引用了一个documentdefaultsdefinition,将来在解析各个bean标签时会起作用,然后调用initdefaults(root, parentdelegate),就是根据父delegate,初始化它自身的defaultsdefinition。
在initdefaults方法中大概就是采用子配置优先的原则给documentdefaultsdefinition属性赋值的,具体就不带大家细看了,不然很容易从入门到放弃,反正经过几层方法的调用,最终进入到这个方法中,这里仅仅贴一下代码。
protected void populatedefaults(documentdefaultsdefinition defaults, @nullable documentdefaultsdefinition parentdefaults, element root) { string lazyinit = root.getattribute(default_lazy_init_attribute); if (isdefaultvalue(lazyinit)) { // potentially inherited from outer <beans> sections, otherwise falling back to false. lazyinit = (parentdefaults != null ? parentdefaults.getlazyinit() : false_value); } defaults.setlazyinit(lazyinit); string merge = root.getattribute(default_merge_attribute); if (isdefaultvalue(merge)) { // potentially inherited from outer <beans> sections, otherwise falling back to false. merge = (parentdefaults != null ? parentdefaults.getmerge() : false_value); } defaults.setmerge(merge); string autowire = root.getattribute(default_autowire_attribute); if (isdefaultvalue(autowire)) { // potentially inherited from outer <beans> sections, otherwise falling back to 'no'. autowire = (parentdefaults != null ? parentdefaults.getautowire() : autowire_no_value); } defaults.setautowire(autowire); if (root.hasattribute(default_autowire_candidates_attribute)) { defaults.setautowirecandidates(root.getattribute(default_autowire_candidates_attribute)); } else if (parentdefaults != null) { defaults.setautowirecandidates(parentdefaults.getautowirecandidates()); } if (root.hasattribute(default_init_method_attribute)) { defaults.setinitmethod(root.getattribute(default_init_method_attribute)); } else if (parentdefaults != null) { defaults.setinitmethod(parentdefaults.getinitmethod()); } if (root.hasattribute(default_destroy_method_attribute)) { defaults.setdestroymethod(root.getattribute(default_destroy_method_attribute)); } else if (parentdefaults != null) { defaults.setdestroymethod(parentdefaults.getdestroymethod()); } defaults.setsource(this.readercontext.extractsource(root)); }
我们知道spring支持配置多个profile,可在beans标签的profile属性配置,然后在运行前动态指定spring.active.profile的。在java项目中,可以配置系统属性system.setproperty("spring.profiles.active","test"),web项目中配置servletcontext上下文参数指定,springboot中也可以通过spring.active.profile指定。
//判断当前beans节点是否是默认命名空间 if (this.delegate.isdefaultnamespace(root)) { //获取beans节点的profile属性 string profilespec = root.getattribute(profile_attribute); if (stringutils.hastext(profilespec)) { //可以使用逗号或分号将当前beans标签指定为多个profile类型 string[] specifiedprofiles = stringutils.tokenizetostringarray( profilespec, beandefinitionparserdelegate.multi_value_attribute_delimiters); // we cannot use profiles.of(...) since profile expressions are not supported // in xml config. see spr-12458 for details. //判断当前beans标签的profile是否被激活 if (!getreadercontext().getenvironment().acceptsprofiles(specifiedprofiles)) { if (logger.isdebugenabled()) { logger.debug("skipped xml bean definition file due to specified profiles [" + profilespec + "] not matching: " + getreadercontext().getresource()); } return; } } }
知道了怎么用,就容易多了,首先得到beans标签的指定的profile数组,与指定的spring.active.profile对比,符合条件的话改beans才会被加载。
首先通过getreadercontext().getenvironment(),获取standardenvironment。这里的getreadercontext()就是获取的刚刚说的xmlreadercontext上下文,再从上下文中的得到xmlbeandefinitionreader初始化时引用的standardenvironment。
下面是xmlbeandefinitionreader继承自抽象父类的构造方法。
protected abstractbeandefinitionreader(beandefinitionregistry registry) { assert.notnull(registry, "beandefinitionregistry must not be null"); this.registry = registry; // determine resourceloader to use. if (this.registry instanceof resourceloader) { this.resourceloader = (resourceloader) this.registry; } else { this.resourceloader = new pathmatchingresourcepatternresolver(); } // inherit environment if possible if (this.registry instanceof environmentcapable) { this.environment = ((environmentcapable) this.registry).getenvironment(); } else { this.environment = new standardenvironment(); } }
这里的standardenvironment初始化会加载当前的系统变量和环境变量,是对系统变量和环境变量的封装。在standardenvironmen继承自父类的构造方法中,会调用customizepropertysources方法
private final mutablepropertysources propertysources = new mutablepropertysources() protected void customizepropertysources(mutablepropertysources propertysources) { propertysources.addlast( new propertiespropertysource(system_properties_property_source_name, getsystemproperties())); propertysources.addlast( new systemenvironmentpropertysource(system_environment_property_source_name, getsystemenvironment())); }
该方法将当前系统变量和环境变量保存在其propertysources属性中。
public class mutablepropertysources implements propertysources { private final list<propertysource<?>> propertysourcelist = new copyonwritearraylist<>(); /** * create a new {@link mutablepropertysources} object. */ public mutablepropertysources() { } }
而mutablepropertysources 有一个list属性,保存多个属性来源。
也就是说,standardenvironment初始化完成时,就会加载系统变量和环境变量,然后这里会调用acceptsprofiles(specifiedprofiles)方法判定当前beans标签的profile是否应该被加载,遍历给定的profiles数组,只要有一个被指定为spring.active.profile就返回true。
public boolean acceptsprofiles(string... profiles) { assert.notempty(profiles, "must specify at least one profile"); for (string profile : profiles) { if (stringutils.haslength(profile) && profile.charat(0) == '!') { if (!isprofileactive(profile.substring(1))) { return true; } } else if (isprofileactive(profile)) { return true; } } return false; } protected boolean isprofileactive(string profile) { validateprofile(profile); set<string> currentactiveprofiles = dogetactiveprofiles(); return (currentactiveprofiles.contains(profile) || (currentactiveprofiles.isempty() && dogetdefaultprofiles().contains(profile))); }
重点是获取spring.active.profile的方法。
protected set<string> dogetactiveprofiles() { synchronized (this.activeprofiles) { if (this.activeprofiles.isempty()) { //从propertysources中获取,key为spring.active.profile string profiles = getproperty(active_profiles_property_name); if (stringutils.hastext(profiles)) { setactiveprofiles(stringutils.commadelimitedlisttostringarray( stringutils.trimallwhitespace(profiles))); } } return this.activeprofiles; } }
经过了委托类的创建,spring.active.profile判断返回true的beans标签,才会进入到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); } }
看到这里应该就一目了然了,判定root节点以及其子节点是否是默认标签,默认标签和自定义标签有不同的解析方式,除了beans、bean、alias和import四种标签外,都是自定义标签,自定义标签需要实现一些接口和配置。如果是默认标签,进入parsedefaultelement方法。
private void parsedefaultelement(element ele, beandefinitionparserdelegate delegate) { //解析import标签 if (delegate.nodenameequals(ele, import_element)) { importbeandefinitionresource(ele); } //解析alias标签 else if (delegate.nodenameequals(ele, alias_element)) { processaliasregistration(ele); } //解析bean标签 else if (delegate.nodenameequals(ele, bean_element)) { processbeandefinition(ele, delegate); } //解析beans标签 else if (delegate.nodenameequals(ele, nested_beans_element)) { // recurse doregisterbeandefinitions(ele); } }
具体的解析过程下一章会讲到,在这里,看看解析beans标签的时候,是不是又会调用doregisterbeandefinitions方法?还记得吗?这正是对之前该方法会递归调用的解释,再贴一遍代码吧。
protected void doregisterbeandefinitions(element root) { // any nested <beans> elements will cause recursion in this method. in // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. beandefinitionparserdelegate parent = this.delegate; //委托给delegate解析 this.delegate = createdelegate(getreadercontext(), root, parent); //判断当前beans节点是否是默认命名空间 if (this.delegate.isdefaultnamespace(root)) { //获取beans节点的profile属性 string profilespec = root.getattribute(profile_attribute); if (stringutils.hastext(profilespec)) { //可以使用逗号或分号将当前beans标签指定为多个profile类型 string[] specifiedprofiles = stringutils.tokenizetostringarray( profilespec, beandefinitionparserdelegate.multi_value_attribute_delimiters); // we cannot use profiles.of(...) since profile expressions are not supported // in xml config. see spr-12458 for details. //判断当前beans标签的profile是否被激活 if (!getreadercontext().getenvironment().acceptsprofiles(specifiedprofiles)) { if (logger.isdebugenabled()) { logger.debug("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; }
走的太远,不要忘记为什么出发!
参考:spring源码深度解析
如对本文有疑问, 点击进行留言回复!!
springmvc之ResponseBody响应json数据遇到的错误及解决
uni-app 后台升级 静默升级 uniapp 后台更新 静默更新 在线升级
SpringBoot多Module启动报错Could not transfer metadata
Hibernate项目报错:Cannot call sendError() after the response has been committed
网友评论