Spring源码分析:Bean加载流程概览及配置文件读取

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

代码入口

之前写文章都会啰啰嗦嗦一大堆再开始,进入【Spring源码分析】这个板块就直接切入正题了。

很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已,Spring的加载过程相对是不太透明的,不太好去找加载的代码入口。

下面有很简单的一段代码可以作为Spring代码加载的入口:

12 ApplicationContextac=newClassPathXmlApplicationContext("spring.xml");ac.getBean(XXX.class);
12 ApplicationContextac=newClassPathXmlApplicationContext("spring.xml");ac.getBean(XXX.class);

ClassPathXmlApplicationContext用于加载CLASSPATH下的Spring配置文件,可以看到,第二行就已经可以获取到Bean的实例了,那么必然第一行就已经完成了对所有Bean实例的加载,因此可以通过ClassPathXmlApplicationContext作为入口。为了后面便于代码阅读,先给出一下ClassPathXmlApplicationContext这个类的继承关系:20191017100404\_1.png

大致的继承关系是如上图所示的,由于版面的关系,没有继续画下去了,左下角的ApplicationContext应当还有一层继承关系,比较关键的一点是它是BeanFactory的子接口。

最后声明一下,本文使用的Spring版本为3.0.7,比较老,使用这个版本纯粹是因为公司使用而已。

ClassPathXmlApplicationContext构造函数

看下ClassPathXmlApplicationContext的构造函数:

1publicClassPathXmlApplicationContext(StringconfigLocation)throwsBeansException{2    this(newString[]{configLocation},true,null);3}
1publicClassPathXmlApplicationContext(StringconfigLocation)throwsBeansException{2    this(newString[]{configLocation},true,null);3}
123456789 publicClassPathXmlApplicationContext(String[]configLocations,booleanrefresh,ApplicationContextparent)        throwsBeansException{     super(parent);    setConfigLocations(configLocations);    if(refresh){        refresh();    }}
123456789 publicClassPathXmlApplicationContext(String[]configLocations,booleanrefresh,ApplicationContextparent)        throwsBeansException{     super(parent);    setConfigLocations(configLocations);    if(refresh){        refresh();    }}

从第二段代码看,总共就做了三件事:

1、super(parent)

没什么太大的作用,设置一下父级ApplicationContext,这里是null

2、setConfigLocations(configLocations)

代码就不贴了,一看就知道,里面做了两件事情:

(1)将指定的Spring配置文件的路径存储到本地

(2)解析Spring配置文件路径中的${PlaceHolder}占位符,替换为系统变量中PlaceHolder对应的Value值,System本身就自带一些系统变量比如class.path、os.name、user.dir等,也可以通过System.setProperty()方法设置自己需要的系统变量

3、refresh()

这个就是整个Spring Bean加载的核心了,它是ClassPathXmlApplicationContext的父类AbstractApplicationContext的一个方法,顾名思义,用于刷新整个Spring上下文信息,定义了整个Spring上下文加载的流程。

refresh方法

上面已经说了,refresh()方法是整个Spring Bean加载的核心,因此看一下整个refresh()方法的定义:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 publicvoidrefresh()throwsBeansException,IllegalStateException{        synchronized(this.startupShutdownMonitor){            //Preparethiscontextforrefreshing.            prepareRefresh();             //Tellthesubclasstorefreshtheinternalbeanfactory.            ConfigurableListableBeanFactorybeanFactory=obtainFreshBeanFactory();             //Preparethebeanfactoryforuseinthiscontext.            prepareBeanFactory(beanFactory);             try{                //Allowspost-processingofthebeanfactoryincontextsubclasses.                postProcessBeanFactory(beanFactory);                 //Invokefactoryprocessorsregisteredasbeansinthecontext.                invokeBeanFactoryPostProcessors(beanFactory);                 //Registerbeanprocessorsthatinterceptbeancreation.                registerBeanPostProcessors(beanFactory);                 //Initializemessagesourceforthiscontext.                initMessageSource();                 //Initializeeventmulticasterforthiscontext.                initApplicationEventMulticaster();                 //Initializeotherspecialbeansinspecificcontextsubclasses.                onRefresh();                 //Checkforlistenerbeansandregisterthem.                registerListeners();                 //Instantiateallremaining(non-lazy-init)singletons.                finishBeanFactoryInitialization(beanFactory);                 //Laststep:publishcorrespondingevent.                finishRefresh();            }             catch(BeansExceptionex){                //Destroyalreadycreatedsingletonstoavoiddanglingresources.                destroyBeans();                 //Reset'active'flag.                cancelRefresh(ex);                 //Propagateexceptiontocaller.                throwex;            }        }    }
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152

每个子方法的功能之后一点一点再分析,首先refresh()方法有几点是值得我们学习的:

1、方法是加锁的,这么做的原因是避免多线程同时刷新Spring上下文

2、尽管加锁可以看到是针对整个方法体的,但是没有在方法前加synchronized关键字,而使用了对象锁startUpShutdownMonitor,这样做有两个好处:

(1)refresh()方法和close()方法都使用了startUpShutdownMonitor对象锁加锁,这就保证了在调用refresh()方法的时候无法调用close()方法,反之亦然,避免了冲突

(2)另外一个好处不在这个方法中体现,但是提一下,使用对象锁可以减小了同步的范围,只对不能并发的代码块进行加锁,提高了整体代码运行的效率

3、方法里面使用了每个子方法定义了整个refresh()方法的流程,使得整个方法流程清晰易懂。这点是非常值得学习的,一个方法里面几十行甚至上百行代码写在一起,在我看来会有三个显著的问题:

(1)扩展性降低。反过来讲,假使把流程定义为方法,子类可以继承父类,可以根据需要重写方法

(2)代码可读性差。很简单的道理,看代码的人是愿意看一段500行的代码,还是愿意看10段50行的代码?

(3)代码可维护性差。这点和上面的类似但又有不同,可维护性差的意思是,一段几百行的代码,功能点不明确,不易后人修改,可能会导致“牵一发而动全身”

prepareRefresh方法

下面挨个看refresh方法中的子方法,首先是prepareRefresh方法,看一下源码:

1234567891011121314 /* Preparethiscontextforrefreshing,settingitsstartupdateand activeflag. /protectedvoidprepareRefresh(){    this.startupDate=System.currentTimeMillis();        synchronized(this.activeMonitor){        this.active=true;    }     if(logger.isInfoEnabled()){        logger.info("Refreshing"+this);    }}
1234567891011121314 /* Preparethiscontextforrefreshing,settingitsstartupdateand activeflag. /protectedvoidprepareRefresh(){    this.startupDate=System.currentTimeMillis();        synchronized(this.activeMonitor){        this.active=true;    }     if(logger.isInfoEnabled()){        logger.info("Refreshing"+this);    }}

这个方法功能比较简单,顾名思义,准备刷新Spring上下文,其功能注释上写了:

1、设置一下刷新Spring上下文的开始时间

2、将active标识位设置为true

另外可以注意一下12行这句日志,这句日志打印了真正加载Spring上下文的Java类。

obtainFreshBeanFactory方法

obtainFreshBeanFactory方法的作用是获取刷新Spring上下文的Bean工厂,其代码实现为:

12345678 protectedConfigurableListableBeanFactoryobtainFreshBeanFactory(){    refreshBeanFactory();    ConfigurableListableBeanFactorybeanFactory=getBeanFactory();    if(logger.isDebugEnabled()){        logger.debug("Beanfactoryfor"+getDisplayName()+":"+beanFactory);    }    returnbeanFactory;}
12345678 protectedConfigurableListableBeanFactoryobtainFreshBeanFactory(){    refreshBeanFactory();    ConfigurableListableBeanFactorybeanFactory=getBeanFactory();    if(logger.isDebugEnabled()){        logger.debug("Beanfactoryfor"+getDisplayName()+":"+beanFactory);    }    returnbeanFactory;}

其核心是第二行的refreshBeanFactory方法,这是一个抽象方法,有AbstractRefreshableApplicationContext和GenericApplicationContext这两个子类实现了这个方法,看一下上面ClassPathXmlApplicationContext的继承关系图即知,调用的应当是AbstractRefreshableApplicationContext中实现的refreshBeanFactory,其源码为:

123456789101112131415161718 protectedfinalvoidrefreshBeanFactory()throwsBeansException{    if(hasBeanFactory()){        destroyBeans();        closeBeanFactory();    }    try{        DefaultListableBeanFactorybeanFactory=createBeanFactory();        beanFactory.setSerializationId(getId());        customizeBeanFactory(beanFactory);        loadBeanDefinitions(beanFactory);        synchronized(this.beanFactoryMonitor){            this.beanFactory=beanFactory;        }    }    catch(IOExceptionex){        thrownewApplicationContextException("I/Oerrorparsingbeandefinitionsourcefor"+getDisplayName(),ex);    }}
123456789101112131415161718 protectedfinalvoidrefreshBeanFactory()throwsBeansException{    if(hasBeanFactory()){        destroyBeans();        closeBeanFactory();    }    try{        DefaultListableBeanFactorybeanFactory=createBeanFactory();        beanFactory.setSerializationId(getId());        customizeBeanFactory(beanFactory);        loadBeanDefinitions(beanFactory);        synchronized(this.beanFactoryMonitor){            this.beanFactory=beanFactory;        }    }    catch(IOExceptionex){        thrownewApplicationContextException("I/Oerrorparsingbeandefinitionsourcefor"+getDisplayName(),ex);    }}

这段代码的核心是第7行,这行点出了DefaultListableBeanFactory这个类,这个类是构造Bean的核心类,这个类的功能会在下一篇文章中详细解读,首先给出DefaultListableBeanFactory的继承关系图:

20191017100404\_2.png

AbstractAutowireCapableBeanFactory这个类的继承层次比较深,版面有限,就没有继续画下去了,本图基本上清楚地展示了DefaultListableBeanFactory的层次结构。

为了更清晰地说明DefaultListableBeanFactory的作用,列举一下DefaultListableBeanFactory中存储的一些重要对象及对象中的内容,DefaultListableBeanFactory基本就是操作这些对象,以表格形式说明:

 对象名 类 型  作  用 归属类
 对象名 类 型  作  用 归属类
 aliasMap Map<String,String> 存储Bean名称->Bean别名映射关系  SimpleAliasRegistry
singletonObjects  Map<String,Object>  存储单例Bean名称->单例Bean实现映射关系 DefaultSingletonBeanRegistry 
 singletonFactories  Map<String,ObjectFactory> 存储Bean名称->ObjectFactory实现映射关系 DefaultSingletonBeanRegistry
earlySingletonObjects  Map<String,Object> 存储Bean名称->预加载Bean实现映射关系  DefaultSingletonBeanRegistry
registeredSingletons Set<String> 存储注册过的Bean名  DefaultSingletonBeanRegistry
singletonsCurrentlyInCreation Set<String> 存储当前正在创建的Bean名  DefaultSingletonBeanRegistry
 disposableBeans  Map<String,Object> 存储Bean名称->Disposable接口实现Bean实现映射关系   DefaultSingletonBeanRegistry
 factoryBeanObjectCache  Map<String,Object> 存储Bean名称->FactoryBean接口Bean实现映射关系 FactoryBeanRegistrySupport
propertyEditorRegistrars  Set<PropertyEditorRegistrar> 存储PropertyEditorRegistrar接口实现集合 AbstractBeanFactory
 embeddedValueResolvers List<StringValueResolver> 存储StringValueResolver(字符串解析器)接口实现列表 AbstractBeanFactory
beanPostProcessors List<BeanPostProcessor> 存储 BeanPostProcessor接口实现列表 AbstractBeanFactory
mergedBeanDefinitions Map<String,RootBeanDefinition> 存储Bean名称->合并过的根Bean定义映射关系 AbstractBeanFactory
 alreadyCreated Set<String> 存储至少被创建过一次的Bean名集合  AbstractBeanFactory
ignoredDependencyInterfaces Set<Class> 存储不自动注入的Class集合 AbstractAutowireCapableBeanFactory
 resolvableDependencies Map<Class,Object> 存储修正过的依赖映射关系 DefaultListableBeanFactory
beanDefinitionMap Map<String,BeanDefinition> 存储Bean名称–>Bean定义映射关系 DefaultListableBeanFactory
beanDefinitionNames List<String> 存储Bean定义名称列表  DefaultListableBeanFactory

XML文件解析

另外一个核心是第10行,loadBeanDefinitions(beanFactory)方法,为什么我们配置的XML文件最后能转成Java Bean,首先就是由这个方法处理的。该方法最终的目的是将XML文件进行解析,以Key-Value的形式,Key表示BeanName,Value为BeanDefinition,最终存入DefaultListableBeanFactory中:

1/*Mapofbeandefinitionobjects,keyedbybeanname/2privatefinalMap<String,BeanDefinition>beanDefinitionMap=newConcurrentHashMap<String,BeanDefinition>();34/*Listofbeandefinitionnames,inregistrationorder/5privatefinalList<String>beanDefinitionNames=newArrayList<String>();
1/*Mapofbeandefinitionobjects,keyedbybeanname/2privatefinalMap<String,BeanDefinition>beanDefinitionMap=newConcurrentHashMap<String,BeanDefinition>();34/*Listofbeandefinitionnames,inregistrationorder/5privatefinalList<String>beanDefinitionNames=newArrayList<String>();

最终DefaultListableBeanFactory会先遍历beanDefinitionNames,从beanDefinitionMap中拿到对应的BeanDefinition,最终转为具体的Bean对象。BeanDefinition本身是一个接口,AbstractBeanDefinition这个抽象类存储了Bean的属性,看一下AbstractBeanDefinition这个抽象类的定义:

20191017100404\_3.png

这个类的属性与方法很多,这里就列举了一些最主要的方法和属性,可以看到包含了bean标签中的所有属性,之后就是根据AbstractBeanDefinition中的属性值构造出对应的Bean对象。

Spring没有直接拿到XML中的bean定义就直接转为具体的Bean对象,就是给Spring开发者留下了扩展点,比如之前BeanPostProcessor,在最后一部分会简单提及。接着看一下XML是如何转为Bean的,首先在AbstractXmlApplicationContext中将DefaultListableBeanFactory转换为XmlBeanDefinitionReader:

1234567891011121314 protectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory)throwsBeansException,IOException{    //CreateanewXmlBeanDefinitionReaderforthegivenBeanFactory.    XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanFactory);     //Configurethebeandefinitionreaderwiththiscontext's    //resourceloadingenvironment.    beanDefinitionReader.setResourceLoader(this);    beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));     //Allowasubclasstoprovidecustominitializationofthereader,    //thenproceedwithactuallyloadingthebeandefinitions.    initBeanDefinitionReader(beanDefinitionReader);    loadBeanDefinitions(beanDefinitionReader);}
1234567891011121314 protectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory)throwsBeansException,IOException{    //CreateanewXmlBeanDefinitionReaderforthegivenBeanFactory.    XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanFactory);     //Configurethebeandefinitionreaderwiththiscontext's    //resourceloadingenvironment.    beanDefinitionReader.setResourceLoader(this);    beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));     //Allowasubclasstoprovidecustominitializationofthereader,    //thenproceedwithactuallyloadingthebeandefinitions.    initBeanDefinitionReader(beanDefinitionReader);    loadBeanDefinitions(beanDefinitionReader);}

XmlBeanDefinitionReader顾名思义,一个XML文件中读取Bean定义的工具,然后追踪13行的代码,先追踪到DefaultBeanDefinitionDocumentReader的parseDefaultElement方法:

1234567891011 privatevoidparseDefaultElement(Elementele,BeanDefinitionParserDelegatedelegate){    if(delegate.nodeNameEquals(ele,IMPORT_ELEMENT)){        importBeanDefinitionResource(ele);    }    elseif(delegate.nodeNameEquals(ele,ALIAS_ELEMENT)){        processAliasRegistration(ele);    }    elseif(delegate.nodeNameEquals(ele,BEAN_ELEMENT)){        processBeanDefinition(ele,delegate);    }}
1234567891011 privatevoidparseDefaultElement(Elementele,BeanDefinitionParserDelegatedelegate){    if(delegate.nodeNameEquals(ele,IMPORT_ELEMENT)){        importBeanDefinitionResource(ele);    }    elseif(delegate.nodeNameEquals(ele,ALIAS_ELEMENT)){        processAliasRegistration(ele);    }    elseif(delegate.nodeNameEquals(ele,BEAN_ELEMENT)){        processBeanDefinition(ele,delegate);    }}

XML文件的节点import、alias、bean分别有自己对应的方法去处理,以最常见的Bean为例,即第9行:

12345678910111213141516 protectedvoidprocessBeanDefinition(Elementele,BeanDefinitionParserDelegatedelegate){    BeanDefinitionHolderbdHolder=delegate.parseBeanDefinitionElement(ele);    if(bdHolder!=null){        bdHolder=delegate.decorateBeanDefinitionIfRequired(ele,bdHolder);        try{            //Registerthefinaldecoratedinstance.            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());        }        catch(BeanDefinitionStoreExceptionex){            getReaderContext().error("Failedtoregisterbeandefinitionwithname'"+                    bdHolder.getBeanName()+"'",ele,ex);        }        //Sendregistrationevent.        getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder));    }}
12345678910111213141516 protectedvoidprocessBeanDefinition(Elementele,BeanDefinitionParserDelegatedelegate){    BeanDefinitionHolderbdHolder=delegate.parseBeanDefinitionElement(ele);    if(bdHolder!=null){        bdHolder=delegate.decorateBeanDefinitionIfRequired(ele,bdHolder);        try{            //Registerthefinaldecoratedinstance.            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());        }        catch(BeanDefinitionStoreExceptionex){            getReaderContext().error("Failedtoregisterbeandefinitionwithname'"+                    bdHolder.getBeanName()+"'",ele,ex);        }        //Sendregistrationevent.        getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder));    }}

核心的解析bean节点的代码为第2行,这里已经调用到了BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法,看下是怎么做的:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 publicBeanDefinitionHolderparseBeanDefinitionElement(Elementele,BeanDefinitioncontainingBean){    Stringid=ele.getAttribute(ID_ATTRIBUTE);    StringnameAttr=ele.getAttribute(NAME_ATTRIBUTE);     List<String>aliases=newArrayList<String>();    if(StringUtils.hasLength(nameAttr)){        String[]nameArr=StringUtils.tokenizeToStringArray(nameAttr,BEAN_NAME_DELIMITERS);        aliases.addAll(Arrays.asList(nameArr));    }     StringbeanName=id;    if(!StringUtils.hasText(beanName)&&!aliases.isEmpty()){        beanName=aliases.remove(0);        if(logger.isDebugEnabled()){            logger.debug("NoXML'id'specified-using'"+beanName+                    "'asbeannameand"+aliases+"asaliases");        }    }     if(containingBean==null){        checkNameUniqueness(beanName,aliases,ele);    }     AbstractBeanDefinitionbeanDefinition=parseBeanDefinitionElement(ele,beanName,containingBean);    if(beanDefinition!=null){        if(!StringUtils.hasText(beanName)){            try{                if(containingBean!=null){                    beanName=BeanDefinitionReaderUtils.generateBeanName(                            beanDefinition,this.readerContext.getRegistry(),true);                }                else{                    beanName=this.readerContext.generateBeanName(beanDefinition);                    //Registeranaliasfortheplainbeanclassname,ifstillpossible,                    //ifthegeneratorreturnedtheclassnameplusasuffix.                    //ThisisexpectedforSpring1.2/2.0backwardscompatibility.                    StringbeanClassName=beanDefinition.getBeanClassName();                    if(beanClassName!=null&&                                beanName.startsWith(beanClassName)&&beanName.length()>beanClassName.length()&&                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)){                        aliases.add(beanClassName);                    }                }                if(logger.isDebugEnabled()){                    logger.debug("NeitherXML'id'nor'name'specified-"+                            "usinggeneratedbeanname["+beanName+"]");                }            }            catch(Exceptionex){                error(ex.getMessage(),ele);                returnnull;            }        }        String[]aliasesArray=StringUtils.toStringArray(aliases);        returnnewBeanDefinitionHolder(beanDefinition,beanName,aliasesArray);    }     returnnull;}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859

总结一下代码逻辑:

(1)第2行和第3行,获取id属性和name属性

(2)第5行~第9行,如果填写了name属性的话,将name属性以”,;”,分割出来的字符串全部认为这个bean的别名,这里我们可以学到Spring的StringUtils的tokenizeToStringArray方法,可以将字符串按照指定分割符分割为字符串数组

(3)第11行~第18行,默认beanName为id属性,如果bean有配置别名(即上面的name属性的话),以name属性的第一个值作为beanName,发现很多人不知道beanName是什么,这几行代码就表示了容器是如何定义beanName的

(4)第20行~第22行,这段用于保证beanName的唯一性的,BeanDefinitionParserDelegate中有一个属性usedNames,这是一个Set,强制性地保证了beanName的唯一性

(5)第24行用于解析bean的其他属性,后面的代码不太重要,看一下parseBeanDefinitionElement的实现

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 publicAbstractBeanDefinitionparseBeanDefinitionElement(        Elementele,StringbeanName,BeanDefinitioncontainingBean){     this.parseState.push(newBeanEntry(beanName));     StringclassName=null;    if(ele.hasAttribute(CLASS_ATTRIBUTE)){        className=ele.getAttribute(CLASS_ATTRIBUTE).trim();    }     try{        Stringparent=null;        if(ele.hasAttribute(PARENT_ATTRIBUTE)){            parent=ele.getAttribute(PARENT_ATTRIBUTE);        }        AbstractBeanDefinitionbd=createBeanDefinition(className,parent);         parseBeanDefinitionAttributes(ele,beanName,containingBean,bd);        bd.setDescription(DomUtils.getChildElementValueByTagName(ele,DESCRIPTION_ELEMENT));         parseMetaElements(ele,bd);        parseLookupOverrideSubElements(ele,bd.getMethodOverrides());        parseReplacedMethodSubElements(ele,bd.getMethodOverrides());         parseConstructorArgElements(ele,bd);        parsePropertyElements(ele,bd);        parseQualifierElements(ele,bd);        bd.setResource(this.readerContext.getResource());        bd.setSource(extractSource(ele));         returnbd;    }    catch(ClassNotFoundExceptionex){        error("Beanclass["+className+"]notfound",ele,ex);    }    catch(NoClassDefFoundErrorerr){        error("Classthatbeanclass["+className+"]dependsonnotfound",ele,err);    }    catch(Throwableex){        error("Unexpectedfailureduringbeandefinitionparsing",ele,ex);    }    finally{        this.parseState.pop();    }     returnnull;}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 publicAbstractBeanDefinitionparseBeanDefinitionElement(        Elementele,StringbeanName,BeanDefinitioncontainingBean){     this.parseState.push(newBeanEntry(beanName));     StringclassName=null;    if(ele.hasAttribute(CLASS_ATTRIBUTE)){        className=ele.getAttribute(CLASS_ATTRIBUTE).trim();    }     try{        Stringparent=null;        if(ele.hasAttribute(PARENT_ATTRIBUTE)){            parent=ele.getAttribute(PARENT_ATTRIBUTE);        }        AbstractBeanDefinitionbd=createBeanDefinition(className,parent);         parseBeanDefinitionAttributes(ele,beanName,containingBean,bd);        bd.setDescription(DomUtils.getChildElementValueByTagName(ele,DESCRIPTION_ELEMENT));         parseMetaElements(ele,bd);        parseLookupOverrideSubElements(ele,bd.getMethodOverrides());        parseReplacedMethodSubElements(ele,bd.getMethodOverrides());         parseConstructorArgElements(ele,bd);        parsePropertyElements(ele,bd);        parseQualifierElements(ele,bd);        bd.setResource(this.readerContext.getResource());        bd.setSource(extractSource(ele));         returnbd;    }    catch(ClassNotFoundExceptionex){        error("Beanclass["+className+"]notfound",ele,ex);    }    catch(NoClassDefFoundErrorerr){        error("Classthatbeanclass["+className+"]dependsonnotfound",ele,err);    }    catch(Throwableex){        error("Unexpectedfailureduringbeandefinitionparsing",ele,ex);    }    finally{        this.parseState.pop();    }     returnnull;}

这里会取class属性、parent属性,18行的代码可以跟进去看一下这里就不贴了,会取得scope、lazy-init、abstract、depends-on属性等等,设置到BeanDefinition中,这样大致上,一个Bean的定义就被存入了BeanDefinition中。最后一步追溯到之前DefaultBeanDefinitionDocumentReader的processBeanDefinition方法:

1 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());
1 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());

这句语句将BeanDefinition存入DefaultListableBeanFactory的beanDefinitionMap中,追踪一下代码最终到DefaultListableBeanFactory的registerBeanDefinition方法中:

12345678910111213141516171819202122232425262728293031323334353637383940 publicvoidregisterBeanDefinition(StringbeanName,BeanDefinitionbeanDefinition)    throwsBeanDefinitionStoreException{     Assert.hasText(beanName,"Beannamemustnotbeempty");    Assert.notNull(beanDefinition,"BeanDefinitionmustnotbenull");     if(beanDefinitioninstanceofAbstractBeanDefinition){        try{            ((AbstractBeanDefinition)beanDefinition).validate();        }        catch(BeanDefinitionValidationExceptionex){            thrownewBeanDefinitionStoreException(beanDefinition.getResourceDescription(),beanName,                    "Validationofbeandefinitionfailed",ex);        }    }     synchronized(this.beanDefinitionMap){        ObjectoldBeanDefinition=this.beanDefinitionMap.get(beanName);        if(oldBeanDefinition!=null){            if(!this.allowBeanDefinitionOverriding){                thrownewBeanDefinitionStoreException(beanDefinition.getResourceDescription(),beanName,                        "Cannotregisterbeandefinition["+beanDefinition+"]forbean'"+beanName+                        "':Thereisalready["+oldBeanDefinition+"]bound.");            }            else{                if(this.logger.isInfoEnabled()){                    this.logger.info("Overridingbeandefinitionforbean'"+beanName+                            "':replacing["+oldBeanDefinition+"]with["+beanDefinition+"]");                }            }        }        else{            this.beanDefinitionNames.add(beanName);            this.frozenBeanDefinitionNames=null;        }        this.beanDefinitionMap.put(beanName,beanDefinition);         resetBeanDefinition(beanName);    }}
12345678910111213141516171819202122232425262728293031323334353637383940

大致上就是beanDefinitionNames中增加一个beanName,beanDefinitionMap将老的BeanDefinition替换(假如不允许BeanDefinition重写的话会抛出异常)。这样一个漫长的流程过后,XML文件中的各个bean节点被转换为BeanDefinition,存入了DefaultListableBeanFactory中,后续DefaultListableBeanFactory可以根据BeanDefinition,构造对应的Bean对象出来。

中不定义id及id重复场景Spring的处理方式

这两天又想到了一个细节问题,中不定义id或者id重复,这两种场景Spring是如何处理的。首先看一下不定义id的场景,代码在BeanDefinitionParserDelegate类第398行的这个判断这里:

1234567891011 if(beanDefinition!=null){    if(!StringUtils.hasText(beanName)){        try{            if(containingBean!=null){                beanName=BeanDefinitionReaderUtils.generateBeanName(                        beanDefinition,this.readerContext.getRegistry(),true);            }            else{                beanName=this.readerContext.generateBeanName(beanDefinition);...}
1234567891011 if(beanDefinition!=null){    if(!StringUtils.hasText(beanName)){        try{            if(containingBean!=null){                beanName=BeanDefinitionReaderUtils.generateBeanName(                        beanDefinition,this.readerContext.getRegistry(),true);            }            else{                beanName=this.readerContext.generateBeanName(beanDefinition);...}

当bean的id未定义时,即beanName为空,进入第2行的if判断。containingBean可以看一下,这里是由方法传入的,是一个null值,因此进入第9行的判断,即beanName由第9行的方法生成,看一下生成方式,代码最终要追踪到BeanDefinitionReaderUtils的generateBeanName方法:

12345678910111213141516171819202122232425262728293031323334 publicstaticStringgenerateBeanName(        BeanDefinitiondefinition,BeanDefinitionRegistryregistry,booleanisInnerBean)        throwsBeanDefinitionStoreException{     StringgeneratedBeanName=definition.getBeanClassName();    if(generatedBeanName==null){        if(definition.getParentName()!=null){            generatedBeanName=definition.getParentName()+"$child";        }        elseif(definition.getFactoryBeanName()!=null){            generatedBeanName=definition.getFactoryBeanName()+"$created";        }    }    if(!StringUtils.hasText(generatedBeanName)){        thrownewBeanDefinitionStoreException("Unnamedbeandefinitionspecifiesneither"+                "'class'nor'parent'nor'factory-bean'-can'tgeneratebeanname");    }     Stringid=generatedBeanName;    if(isInnerBean){        //Innerbean:generateidentityhashcodesuffix.        id=generatedBeanName+GENERATED_BEAN_NAME_SEPARATOR+ObjectUtils.getIdentityHexString(definition);    }    else{        //Top-levelbean:useplainclassname.        //Increasecounteruntiltheidisunique.        intcounter=-1;        while(counter==-1 registry.containsBeanDefinition(id)){            counter++;            id=generatedBeanName+GENERATED_BEAN_NAME_SEPARATOR+counter;        }    }    returnid;}
12345678910111213141516171819202122232425262728293031323334 publicstaticStringgenerateBeanName(        BeanDefinitiondefinition,BeanDefinitionRegistryregistry,booleanisInnerBean)        throwsBeanDefinitionStoreException{     StringgeneratedBeanName=definition.getBeanClassName();    if(generatedBeanName==null){        if(definition.getParentName()!=null){            generatedBeanName=definition.getParentName()+"$child";        }        elseif(definition.getFactoryBeanName()!=null){            generatedBeanName=definition.getFactoryBeanName()+"$created";        }    }    if(!StringUtils.hasText(generatedBeanName)){        thrownewBeanDefinitionStoreException("Unnamedbeandefinitionspecifiesneither"+                "'class'nor'parent'nor'factory-bean'-can'tgeneratebeanname");    }     Stringid=generatedBeanName;    if(isInnerBean){        //Innerbean:generateidentityhashcodesuffix.        id=generatedBeanName+GENERATED_BEAN_NAME_SEPARATOR+ObjectUtils.getIdentityHexString(definition);    }    else{        //Top-levelbean:useplainclassname.        //Increasecounteruntiltheidisunique.        intcounter=-1;        while(counter==-1 registry.containsBeanDefinition(id)){            counter++;            id=generatedBeanName+GENERATED_BEAN_NAME_SEPARATOR+counter;        }    }    returnid;}

这段代码的逻辑很容易看懂,即:

  • 假如是innerBean(比如Spring AOP产生的Bean),使用【类全路径+#+对象HashCode的16进制】的格式来命名Bean
  • 假如不是innerBean,使用【类全路径+#+数字】的格式来命名Bean,其中数字指的是,同一个Bean出现1次,只要该Bean没有id,就从0开始依次向上累加,比如a.b.c#0、a.b.c#1、a.b.c#2

接着看一下id重复的场景Spring的处理方式,重复id是这样的,Spring使用XmlBeanDefinitionReader读取xml文件,在这个类的doLoadBeanDefinitions的方法中:

12345678910111213 protectedintdoLoadBeanDefinitions(InputSourceinputSource,Resourceresource)    throwsBeanDefinitionStoreException{    try{        intvalidationMode=getValidationModeForResource(resource);        Documentdoc=this.documentLoader.loadDocument(                inputSource,getEntityResolver(),this.errorHandler,validationMode,isNamespaceAware());        returnregisterBeanDefinitions(doc,resource);    }    catch(BeanDefinitionStoreExceptionex){        throwex;    }    ...}
12345678910111213 protectedintdoLoadBeanDefinitions(InputSourceinputSource,Resourceresource)    throwsBeanDefinitionStoreException{    try{        intvalidationMode=getValidationModeForResource(resource);        Documentdoc=this.documentLoader.loadDocument(                inputSource,getEntityResolver(),this.errorHandler,validationMode,isNamespaceAware());        returnregisterBeanDefinitions(doc,resource);    }    catch(BeanDefinitionStoreExceptionex){        throwex;    }    ...}

第5行的代码将xml解析成Document,这里的解析使用的是JDK自带的DocumentBuilder,DocumentBuilder处理xml文件输入流,发现两个中定义的id重复即会抛出XNIException异常,最终将导致Spring容器启动失败。

因此,结论就是:Spring不允许两个定义相同的id


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring源码分析:Bean加载流程概览及配置文件读取

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏