当前位置: 移动技术网 > IT编程>开发语言>Java > 通俗理解spring源码(五)—— 解析及注册BeanDefinitions

通俗理解spring源码(五)—— 解析及注册BeanDefinitions

2020年05月04日  | 移动技术网IT编程  | 我要评论

通俗理解spring源码(五)—— 解析及注册beandefinitions

  上节讲到了如何获取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的默认属性。

1、创建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));
    }

2、判断当前profile是否被激活

  我们知道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;
        }
    }

3、解析根节点parsebeandefinitions

  经过了委托类的创建,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源码深度解析

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网