Spring源码加载BeanDefinition过程
本文主要讲解spring加载xml配置文件的方式,跟踪加载beandefinition的全过程。
源码分析
源码的入口
classpathxmlapplicationcontext构造函数
new classpathxmlapplicationcontext(“spring.xml”)用于加载classpath下的spring配置文件,将配置文件传给构造函数,然后调用类内部的另外一个重载方法。
从构造函数中,可以看到一共做了3件事
super(parent)
super(parent)的作用是为容器设置bean资源加载器,层层跟踪,可知实际是由其父类abstractapplicationcontext完成设置的,parent为null,setparent(parent)就不继续跟踪了,这里需要注意的是,该类继承了defaultresourceloader,所以该类也作为资源加载器
abstractapplicationcontext.java
跟踪该类this()无参构造函数进去看看
abstractapplicationcontext.java
abstractapplicationcontext.java
pathmatchingresourcepatternresolver.java
setconfiglocations(configlocations)
设置bean定义资源的路径,由其父类abstractrefreshableconfigapplicationcontext完成,resolvepath解析路径,一直跟踪到底层是调用propertyplaceholderhelper的parsestringvalue完成设置的
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(); } } }
总结:
- 方法加了个类锁,避免了多线程同时刷新spring上下文。
-
锁的这个对象为this.startupshutdownmonitor,有两个好处
- refresh()方法和close()方法都使用了this.startupshutdownmonitor,保证了在调用refresh()方法的时候无法使用close()方法,反之亦然,避免了冲突
- 使用对象锁可以减少同步的范围,只对不能并发的代码块进行加锁,提高了整体代码的运行效率。
- refresh()函数是一个模板方法,执行多个方法,而且提供了各个protected方法(默认实现),其子类可以重写他们。
- 模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类(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)
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();
推荐阅读
-
基于Spring注解的上下文初始化过程源码解析(一)
-
spring源码分析6: ApplicationContext的初始化与BeanDefinition的搜集入库
-
Spring源码剖析2:Spring IOC容器的加载过程
-
Tomcat源码分析三:Tomcat启动加载过程(一)的源码解析
-
spring5 源码深度解析-----ApplicationContext容器refresh过程
-
Spring MVC源码(一) ----- 启动过程与组件初始化
-
spring源码深度解析— IOC 之 开启 bean 的加载
-
Spring5.0源码学习系列之浅谈懒加载机制原理
-
深入理解 Tomcat(五)源码剖析Tomcat 启动过程----类加载过程
-
Spring IOC 加载过程(一 加载xml)