欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring源码加载BeanDefinition过程

程序员文章站 2022-04-10 14:05:23
本文主要讲解Spring加载xml配置文件的方式,跟踪加载BeanDefinition的全过程。 源码分析 源码的入口 ClassPathXmlApplicationContext构造函数 new ClassPathXmlApplicationContext(“spring.xml”)用于加载CLA ......

  本文主要讲解spring加载xml配置文件的方式,跟踪加载beandefinition的全过程。

源码分析

源码的入口

Spring源码加载BeanDefinition过程

classpathxmlapplicationcontext构造函数

  new classpathxmlapplicationcontext(“spring.xml”)用于加载classpath下的spring配置文件,将配置文件传给构造函数,然后调用类内部的另外一个重载方法。

Spring源码加载BeanDefinition过程

Spring源码加载BeanDefinition过程

 

 

从构造函数中,可以看到一共做了3件事

super(parent)

  super(parent)的作用是为容器设置bean资源加载器,层层跟踪,可知实际是由其父类abstractapplicationcontext完成设置的,parent为null,setparent(parent)就不继续跟踪了,这里需要注意的是,该类继承了defaultresourceloader,所以该类也作为资源加载器

abstractapplicationcontext.java

Spring源码加载BeanDefinition过程

 

 

跟踪该类this()无参构造函数进去看看

abstractapplicationcontext.java

Spring源码加载BeanDefinition过程

 

 

abstractapplicationcontext.java

Spring源码加载BeanDefinition过程

 

 

 pathmatchingresourcepatternresolver.java

Spring源码加载BeanDefinition过程

setconfiglocations(configlocations)

  设置bean定义资源的路径,由其父类abstractrefreshableconfigapplicationcontext完成,resolvepath解析路径,一直跟踪到底层是调用propertyplaceholderhelper的parsestringvalue完成设置的

Spring源码加载BeanDefinition过程

 

 

 refresh()

  这个就是整个spring bean加载的核心里面十二大步,用于刷新整个spring上下文信息,定义了整个spring上下文加载的流程。

@override
    public void refresh() throws beansexception, illegalstateexception {
        synchronized (this.startupshutdownmonitor) {
            //1、 prepare this context for refreshing.
            preparerefresh();

               //创建defaultlistablebeanfactory(真正生产和管理bean的容器)
               //加载beandefition并注册到beandefitionregistry
               //通过namespacehandler解析自定义标签的功能(比如:context标签、aop标签、tx标签)
            //2、 tell the subclass to refresh the internal bean factory.
            configurablelistablebeanfactory beanfactory = obtainfreshbeanfactory();

            //3、 prepare the bean factory for use in this context.
            preparebeanfactory(beanfactory);

            try {
                //4、 allows post-processing of the bean factory in context subclasses.
                postprocessbeanfactory(beanfactory);

                     //实例化并调用实现了beanfactorypostprocessor接口的bean
                     //比如:propertyplaceholderconfigurer(context:property-placeholer)
                     //就是此处被调用的,作用是替换掉beandefinition中的占位符(${})中的内容
                //5、 invoke factory processors registered as beans in the context.
                invokebeanfactorypostprocessors(beanfactory);

                     //创建并注册beanpostprocessor到beanfactory中(bean的后置处理器)
                     //比如:autowiredannotationbeanpostprocessor(实现@autowired注解功能)
                     //      requiredannotationbeanpostprocessor(实现@d注解功能)
                     //这些注册的beanpostprocessor
                //6、 register bean processors that intercept bean creation.
                registerbeanpostprocessors(beanfactory);

                //7、 initialize message source for this context.
                initmessagesource();

                //8、 initialize event multicaster for this context.
                initapplicationeventmulticaster();

                //9、 initialize other special beans in specific context subclasses.
                onrefresh();

                //10、 check for listener beans and register them.
                registerlisteners();

                     //创建非懒加载方式的单例bean实例(未设置属性)
                     //填充属性
                     //初始化实例(比如调用init-method方法)
                     //调用beanpostprocessor(后置处理器)对实例bean进行后置处理
                //11、 instantiate all remaining (non-lazy-init) singletons.
                finishbeanfactoryinitialization(beanfactory);

                //12、 last step: publish corresponding event.
                finishrefresh();
            }

            catch (beansexception ex) {
                if (logger.iswarnenabled()) {
                    logger.warn("exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // destroy already created singletons to avoid dangling resources.
                destroybeans();

                // reset 'active' flag.
                cancelrefresh(ex);

                // propagate exception to caller.
                throw ex;
            }

            finally {
                // reset common introspection caches in spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetcommoncaches();
            }
        }
    }

总结:

  1. 方法加了个类锁避免多线程同时刷新spring上下文
  2. 的这个对象this.startupshutdownmonitor,有两个好处
    1. refresh()方法和close()方法使this.startupshutdownmonitor保证在调用refresh()方法的时候无法使用close()方法,反之亦然,避免了冲突
    2. 使用对象锁可以减少同步的范围,只对不能并发的代码块进行加锁,提高整体代码运行效率
  3. refresh()函数一个模板方法,执行多个方法,而且提供了各个protected方法(默认实现),其子类可以重写他们
  4. 模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类(protected方法)可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

refresh()核心调用obtainfreshbeanfactory()

  obtainfreshbeanfactory()函数调用,完成了容器初始化的最基础的功能,bean定义资源的resource定位、加载解析和注册

abstractapplicationcontext.java

    protected configurablelistablebeanfactory obtainfreshbeanfactory() {
        //使用的委派模式,调用2个抽象方法,定义了obtainfreshbeanfactory的算法骨架,实际的行为交给了子类abstractrefreshableapplicationcontext实现
        refreshbeanfactory();
        configurablelistablebeanfactory beanfactory = getbeanfactory();
        if (logger.isdebugenabled()) {
            logger.debug("bean factory for " + getdisplayname() + ": " + beanfactory);
        }
        return beanfactory;
    }

abstractrefreshableapplicationcontext.java

    /**
     * this implementation performs an actual refresh of this context's underlying
     * bean factory, shutting down the previous bean factory (if any) and
     * initializing a fresh bean factory for the next phase of the context's lifecycle.
     */
    @override
    protected final void refreshbeanfactory() throws beansexception {
         //若有容器,销毁容器中的bean,关闭容器,以此保证refresh()之后使用的是新建立起来的ioc容器
        if (hasbeanfactory()) {
            destroybeans();
            closebeanfactory();
        }
        try {
            //创建ioc容器
            defaultlistablebeanfactory beanfactory = createbeanfactory();
            beanfactory.setserializationid(getid());
            customizebeanfactory(beanfactory);
            //调用加载bean定义的方法,使用了委派模式,在当前类中定义了抽象的loadbeandefinitions方法,具体实现交给子类
            loadbeandefinitions(beanfactory);
            synchronized (this.beanfactorymonitor) {
                this.beanfactory = beanfactory;
            }
        }
        catch (ioexception ex) {
            throw new applicationcontextexception("i/o error parsing bean definition source for " + getdisplayname(), ex);
        }
    }st

  这个方法中先判断beanfactory是否存在,若存在,则先销毁并关闭beanfactory接着创建defaultlistablebeanfactory,并调用loadbeandefinitions装在bean使用了委派模式,在当前类中定义抽象的loadbeandefinitions方法,具体的实现交给子类abstractxmlapplicationcontext

abstractxmlapplicationcontext.java

    /**
     * loads the bean definitions via an xmlbeandefinitionreader.
     * @see org.springframework.beans.factory.xml.xmlbeandefinitionreader
     * @see #initbeandefinitionreader
     * @see #loadbeandefinitions
     */
    @override
    protected void loadbeandefinitions(defaultlistablebeanfactory beanfactory) throws beansexception, ioexception {
        // 为给定的bean工厂创建一个新的xmlbeanfinitionreader
        xmlbeandefinitionreader beandefinitionreader = new xmlbeandefinitionreader(beanfactory);
        //使用此上下文的bean定义读取器配置
        // 资源加载环境
        beandefinitionreader.setenvironment(this.getenvironment());
        beandefinitionreader.setresourceloader(this);
        beandefinitionreader.setentityresolver(new resourceentityresolver(this));

        // 允许子类提供读取器的自定义初始化,然后继续实际加载bean定义
        initbeandefinitionreader(beandefinitionreader);
        loadbeandefinitions(beandefinitionreader);
    }

看下new xmlbeandefinitionreader(beanfactory)做了哪些工作,底层初始化了beandefinitionregistry=beandefinitionregistry也就是this.registry = registry

xmlbeandefinitionreader.java

    /**
     * create new xmlbeandefinitionreader for the given bean factory.
     * @param registry the beanfactory to load bean definitions into,
     * in the form of a beandefinitionregistry
     */
    public xmlbeandefinitionreader(beandefinitionregistry registry) {
        super(registry);
    }

abstractbeandefinitionreader.java

    /**
     * create a new abstractbeandefinitionreader for the given bean factory.
     * <p>if the passed-in bean factory does not only implement the beandefinitionregistry
     * interface but also the resourceloader interface, it will be used as default
     * resourceloader as well. this will usually be the case for
     * {@link org.springframework.context.applicationcontext} implementations.
     * <p>if given a plain beandefinitionregistry, the default resourceloader will be a
     * {@link org.springframework.core.io.support.pathmatchingresourcepatternresolver}.
     * <p>if the passed-in bean factory also implements {@link environmentcapable} its
     * environment will be used by this reader.  otherwise, the reader will initialize and
     * use a {@link standardenvironment}. all applicationcontext implementations are
     * environmentcapable, while normal beanfactory implementations are not.
     * @param registry the beanfactory to load bean definitions into,
     * in the form of a beandefinitionregistry
     * @see #setresourceloader
     * @see #setenvironment
     */
    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();
        }
    }

接着看abstractxmlapplicationcontext下的loadbeandefinitions(defaultlistablebeanfactory beanfactory)方法,看最下面一行的loadbeandefinitions(beandefinitionreader)

Spring源码加载BeanDefinition过程

 

 abstractxmlapplicationcontext.java

    /**
     * load the bean definitions with the given xmlbeandefinitionreader.
     * <p>the lifecycle of the bean factory is handled by the {@link #refreshbeanfactory}
     * method; hence this method is just supposed to load and/or register bean definitions.
     * @param reader the xmlbeandefinitionreader to use
     * @throws beansexception in case of bean registration errors
     * @throws ioexception if the required xml document isn't found
     * @see #refreshbeanfactory
     * @see #getconfiglocations
     * @see #getresources
     * @see #getresourcepatternresolver
     */
    protected void loadbeandefinitions(xmlbeandefinitionreader reader) throws beansexception, ioexception {
        resource[] configresources = getconfigresources();
        if (configresources != null) {
            reader.loadbeandefinitions(configresources);
        }
        string[] configlocations = getconfiglocations();
        if (configlocations != null) {
            reader.loadbeandefinitions(configlocations);
        }
    }

接着跟踪第一个reader.loadbeandefinitions(configresources)

abstractbeandefinitionreader.java

    @override
    public int loadbeandefinitions(resource... resources) throws beandefinitionstoreexception {
        assert.notnull(resources, "resource array must not be null");
        int counter = 0;
        for (resource resource : resources) {
            counter += loadbeandefinitions(resource);
        }
        return counter;
    }

开始循环加载loadbeandefinitions,继续跟踪loadbeandefinitions方法

xmlbeandefinitionreader.java

    /**
     * load bean definitions from the specified xml file.
     * @param resource the resource descriptor for the xml file
     * @return the number of bean definitions found
     * @throws beandefinitionstoreexception in case of loading or parsing errors
     */
    @override
    public int loadbeandefinitions(resource resource) throws beandefinitionstoreexception {
        return loadbeandefinitions(new encodedresource(resource));
    }

继续跟踪loadbeandefinitions(new encodedresource(resource))

xmlbeandefinitionreader.java

    /**
     * load bean definitions from the specified xml file.
     * @param encodedresource the resource descriptor for the xml file,
     * allowing to specify an encoding to use for parsing the file
     * @return the number of bean definitions found
     * @throws beandefinitionstoreexception in case of loading or parsing errors
     */
    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<>(4);
            this.resourcescurrentlybeingloaded.set(currentresources);
        }
        if (!currentresources.add(encodedresource)) {
            throw new beandefinitionstoreexception(
                    "detected cyclic loading of " + encodedresource + " - check your import definitions!");
        }
        try {
            inputstream inputstream = encodedresource.getresource().getinputstream();
            try {
                inputsource inputsource = new inputsource(inputstream);
                if (encodedresource.getencoding() != null) {
                    inputsource.setencoding(encodedresource.getencoding());
                }
                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();
            }
        }
    }

利用currentresources.add(encodedresource)用set判断,如果重复加载资源就抛出异常,继续跟踪doloadbeandefinitions(inputsource, encodedresource.getresource())

xmlbeandefinitionreader.java

    /**
     * actually load bean definitions from the specified xml file.
     * @param inputsource the sax inputsource to read from
     * @param resource the resource descriptor for the xml file
     * @return the number of bean definitions found
     * @throws beandefinitionstoreexception in case of loading or parsing errors
     * @see #doloaddocument
     * @see #registerbeandefinitions
     */
    protected int doloadbeandefinitions(inputsource inputsource, resource resource)
            throws beandefinitionstoreexception {
        try {
            document doc = doloaddocument(inputsource, resource);
            return registerbeandefinitions(doc, resource);
        }
        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);
        }
    }

doloaddocument(inputsource, resource)将xml解析成org.w3c.dom,具体底层如何实现,自行跟踪,主要看registerbeandefinitions(doc, resource)

xmlbeandefinitionreader.java

    /**
     * register the bean definitions contained in the given dom document.
     * called by {@code loadbeandefinitions}.
     * <p>creates a new instance of the parser class and invokes
     * {@code registerbeandefinitions} on it.
     * @param doc the dom document
     * @param resource the resource descriptor (for context information)
     * @return the number of bean definitions found
     * @throws beandefinitionstoreexception in case of parsing errors
     * @see #loadbeandefinitions
     * @see #setdocumentreaderclass
     * @see beandefinitiondocumentreader#registerbeandefinitions
     */
    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;
    }

继续跟踪documentreader.registerbeandefinitions(doc, createreadercontext(resource))

defaultbeandefinitiondocumentreader.java

    /**
     * this implementation parses bean definitions according to the "spring-beans" xsd
     * (or dtd, historically).
     * <p>opens a dom document; then initializes the default settings
     * specified at the {@code <beans/>} level; then parses the contained bean definitions.
     */
    @override
    public void registerbeandefinitions(document doc, xmlreadercontext readercontext) {
        this.readercontext = readercontext;
        logger.debug("loading bean definitions");
        element root = doc.getdocumentelement();
        doregisterbeandefinitions(root);
    }

继续跟踪doregisterbeandefinitions(root)

defaultbeandefinitiondocumentreader.java

    /**
     * register each bean definition within the given root {@code <beans/>} element.
     */
    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;
        //初始化bean默认的解析器beandefinitionparserdelegate
        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)) {
                    if (logger.isinfoenabled()) {
                        logger.info("skipped xml bean definition file due to specified profiles [" + profilespec +
                                "] not matching: " + getreadercontext().getresource());
                    }
                    return;
                }
            }
        }

        preprocessxml(root);
        //解析dom
        parsebeandefinitions(root, this.delegate);
        postprocessxml(root);

        this.delegate = parent;
    }

createdelegate(getreadercontext(), root, parent)初始化bean默认的解析器,beandefinitionparserdelegate开始解析dom,前面各有一个预留的空方法,方便以后版本扩展,继续跟踪parsebeandefinitions(root, this.delegate)

defaultbeandefinitiondocumentreader.java

    /**
     * parse the elements at the root level in the document:
     * "import", "alias", "bean".
     * @param root the dom root element of the document
     */
    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);
        }
    }

获取节点的命名空间,判断是不是spring默认的,是的话就执行parsedefaultelement(ele, delegate),不是的话,就执行delegate.parsecustomelement(root),跟踪parsedefaultelement(ele, delegate)

defaultbeandefinitiondocumentreader.java

    private void parsedefaultelement(element ele, beandefinitionparserdelegate delegate) {
        if (delegate.nodenameequals(ele, import_element)) { //import
            importbeandefinitionresource(ele);
        }
        else if (delegate.nodenameequals(ele, alias_element)) { //alias
            processaliasregistration(ele);
        }
        else if (delegate.nodenameequals(ele, bean_element)) { //bean
            processbeandefinition(ele, delegate);
        }
        else if (delegate.nodenameequals(ele, nested_beans_element)) { //beans
            // recurse
            doregisterbeandefinitions(ele);
        }
    }

标签分别是import、alias、bean、beans,至此beandefinition加载完成,这就是refresh()方法中的

configurablelistablebeanfactory beanfactory = obtainfreshbeanfactory();