作為一個java程序員颅痊,保守估計一年里也都有300天要和Spring有親密接觸~~像我這種怕是每天都要擼擼Spring逢捺,所以這次也要做個深入了解男窟!這次就來看看Spring是怎么初始化IoC容器的??
注:閱讀本文時一定要在IDE進行跳轉
我們都是知道Spring為企業(yè)級應用提供了豐富的功能式镐,而這些功能底層都依賴于底層最核心的兩種特性IOC和AOP韭畸。
IOC實現(xiàn)里主要包括兩部分丈积,一個是IOC容器初始化穷缤,另外一個是依賴注入夫壁,由于兩部分是相對獨立的部分,所以分成不同文章講解庆械,本篇主要講述IOC容器的初始化薇溃。
一、IoC的概念
控制反轉(Inversion of Control缭乘,縮寫為IoC)沐序,是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度堕绩。其中最常見的方式叫做依賴注入(Dependency Injection策幼,簡稱DI),還有一種方式叫“依賴查找”(Dependency Lookup)逛尚。通過控制反轉垄惧,對象在被創(chuàng)建的時候,由一個調(diào)控系統(tǒng)內(nèi)所有對象的外界實體绰寞,將其所依賴的對象的引用傳遞給它到逊。也可以說,依賴被注入到對象中滤钱。
上面這個概念是來自維基百科觉壶,在Expert Spring MVC and Web Flow和Expert One-on-One J2EE without EJB等書中都也是將依賴注入看作是IoC的一種方式。不過有些地方會把IoC和DI看成一個概念(例如Spring in Action件缸、Spring揭密等書)铜靶,不過沒關系,不影響我們理解就可以他炊。
白話版
A類的正常的運行需要B類
沒有IoC之前我們在使用A類的某個方法時争剿,總會依賴于B類的一些功能,這樣我們就要去new個B對象痊末,有時還要考慮什么時候銷毀蚕苇,什么時候使用單例模式等等,類少了還好管理凿叠,這要是多起來真是再聰明的人怕也是要十個頭九個大了涩笤, 而且A、B之間的依賴關系使各代碼緊密耦合盒件,一旦B類的出現(xiàn)問題蹬碧,或者某天干脆不用B了用C,那是不是A類里的new B()全得換成new C()炒刁?想象都覺得累...
有了IoC之后恩沽,對象創(chuàng)建都交給第三方容器處理,A中的B通過注入來完成翔始,B出問題了飒筑,或者需要換成C了片吊,只要把注入的B換成C就可以(現(xiàn)實開發(fā)中B、C可以實現(xiàn)相同的接口解決~所以啊协屡,Spring是面向接口編程鴨)。
Tips
Expert One-on-One J2EE without EJB這本書是spring爸爸Rod Johnson寫的全谤,進入Spring的BeanFactory類里面看看作者就是他肤晓,哈哈!
淺談控制反轉與依賴注入:這是我看過最好的一篇對控制反轉的解釋认然,強烈推薦补憾!
二、IoC容器初始化
預備內(nèi)容
本節(jié)只講解IoC容器的初始化卷员,其中包括創(chuàng)建容器和將bean裝入到容器中盈匾,下面這三件事是該部分的核心:
BeanDefinition的Resource定位
BeanDefinition的載入和解析
BeanDefinition在容器中的注冊
因為Spring的IoC容器實現(xiàn)太復雜了,各種類之間的調(diào)用很容易就讓我們陷入到細節(jié)之中毕骡,結果就走的太遠忘記了為啥要出發(fā)了??削饵,本文主要將述容器初始化時最主要的三件事。
先了解幾個概念:
BeanFactory:這是IOC容器的接口定義未巫,提供了IoC最基本的功能窿撬,如果說容器是個汽車工廠,那么這個結構就規(guī)定了汽車工廠最基本的功能叙凡,能儲存零件劈伴,能組裝汽車。
publicinterfaceBeanFactory{/**
? ? * 使用容器獲取bean時握爷,添加轉義自符&可以獲取到FactoryBean本身而吧是Factory產(chǎn)生的對象
? ? */String FACTORY_BEAN_PREFIX ="&";/**
? ? * 通過bean的名字來獲取bean
? ? */ObjectgetBean(String name)throwsBeansException;/**
? ? * 通過bean的類型和類型來獲取bean
? ? */TgetBean(String name, Class<T> requiredType)throwsBeansException;/**
? ? * 通過bean的類型來獲取bean
? ? */TgetBean(Class<T> requiredType)throwsBeansException;/**
? ? * 通過名字和參數(shù)來獲取bean
? ? */ObjectgetBean(String name, Object... args)throwsBeansException;/**
? ? * 是否包含名字為name的bean
? ? */booleancontainsBean(String name);/**
? ? * 是否單例
? ? */booleanisSingleton(String name)throwsNoSuchBeanDefinitionException;/**
? ? * 是否原型
? ? */booleanisPrototype(String name)throwsNoSuchBeanDefinitionException;/**
? ? * 名字為name的bean是否是targetType類型
? ? */booleanisTypeMatch(String name, Class<?> targetType)throwsNoSuchBeanDefinitionException;/**
? ? * 獲取名字為name的bean類型
? ? */Class getType(String name)throwsNoSuchBeanDefinitionException;/**
? ? * 獲取名字為name的bean的別名字集合
? ? */String[] getAliases(String name);}
ApplicationContext:升級版汽車廠跛璧,除了上面的功能,還提供很多人性化的服務新啼,繼承了 MessageSource追城,ResourceLoader,ApplicationEventPublisher等等接口师抄,在BeanFactory 簡單IoC容器的基礎上添加了許多對高級容器的支持漓柑。
publicinterfaceApplicationContextextendsEnvironmentCapable,ListableBeanFactory,HierarchicalBeanFactory,MessageSource,ApplicationEventPublisher,ResourcePatternResolver{/**
? ? * 返回該上下文的id(unique)
? ? */StringgetId();/**
? ? *? 返回上下文所屬應用的名字
? ? */StringgetApplicationName();/**
? ? * 返回這個上下文友好的名字
? ? */StringgetDisplayName();/**
? ? * 返回上下文首次加載的時間
? ? */longgetStartupDate();/**
? ? * 返回父類上下文
? ? */ApplicationContextgetParent();/**
? ? * 功能性的暴露自動裝配的工廠,并不常用
? ? */AutowireCapableBeanFactorygetAutowireCapableBeanFactory()throwsIllegalStateException;}
這里面的方法也不多叨吮,主要的方法都在繼承的接口里
BeanDifinition:儲存 Spring中 Bean的信息辆布,包括各屬性名,類名茶鉴,是否單例等锋玲,抽象了我們對 Bean的定義,是讓容器起作用的主要數(shù)據(jù)類型涵叮。對 IOC 容器來說惭蹂,BeanDefinition 就是對控制反轉模式中管理的對象依賴關系的數(shù)據(jù)抽象伞插。
接下來正式進入IoC容器初始化的分析,以FileSystemXmlApplicationContext為例,下面是FileSystemXmlApplicationContext的繼承關系~(這形狀盾碗,滿滿的愛啊媚污,哈哈)
BeanDefinition的Resource定位
publicclassFileSystemXmlApplicationContextextendsAbstractXmlApplicationContext{/**
? ? * 無參構造
? ? */publicFileSystemXmlApplicationContext(){? ? }/**
? ? * 傳入父類上下文
? ? */publicFileSystemXmlApplicationContext(ApplicationContext parent){super(parent);? ? }/**
? ? * 核心構造方法,其他幾個都基于本構造方法
? ? * configLocations 傳入xml配置文件位置集合
? ? * refresh 是否自動刷新容器(是refresh方法的調(diào)用廷雅,初始化上下文的核心方法)
? ? * parent 父類上下文
? ? * 1.傳入配置文件地址
? ? * 2.刷新容器
? ? */publicFileSystemXmlApplicationContext(String[] configLocations,booleanrefresh, ApplicationContext parent)throwsBeansException{super(parent);? ? ? ? setConfigLocations(configLocations);if(refresh) {? ? ? ? ? ? refresh();? ? ? ? }? ? }/**
? ? * 通過給定的路徑在文件系統(tǒng)中定位BeanDefinition并返回一個FileSystemResource
? ? * 這個方法是BeanDefinitionReader的loadBeanDefinition中被調(diào)用耗美,
? ? * loadBeanDefinition采用了模板模式,具體實現(xiàn)在不同的子類中(默認是類路徑)
? ? */@OverrideprotectedResourcegetResourceByPath(String path){if(path !=null&& path.startsWith("/")) {? ? ? ? ? ? path = path.substring(1);? ? ? ? }returnnewFileSystemResource(path);? ? }}
看以看出航缀,本類對所有configLocation都進行了處理商架,使所有以xml形式存在的BeanDefinition都得到了處理,其中這個refresh就最最關鍵點方法芥玉,接下來對refresh進行解析蛇摸。
refresh是在AbstractApplicationContext中實現(xiàn),理解了refresh方法灿巧,基本就理解了IoC初始化的全過程了赶袄。
publicvoidrefresh()throwsBeansException, IllegalStateException{synchronized(this.startupShutdownMonitor) {// 刷新前準備活動prepareRefresh();// 關鍵方法構建beanFactory——>接下來會詳解本方法ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 為在這個上下文中使用beanFactory做準備prepareBeanFactory(beanFactory);try{// 設置后置處理器postProcessBeanFactory(beanFactory);// 調(diào)用bean的后置處理器,這些處理器在上下文中被注冊為bean的形式// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.// 注冊攔截bean創(chuàng)建的處理器registerBeanPostProcessors(beanFactory);// Initialize message source for this context.// 為上下文初始化消息源砸烦,國際化功能initMessageSource();// Initialize event multicaster for this context.// 初始化上下文的時間機制initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.// 初始化其他特殊bean在特殊上下文子類中onRefresh();// Check for listener beans and register them.// 檢查監(jiān)聽的bean弃鸦,并將他們注冊到容器中registerListeners();// Instantiate all remaining (non-lazy-init) singletons.// 初始化所有的非懶加載單件finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.// 發(fā)布相關事件,結束refreshfinishRefresh();? ? ? ? }catch(BeansException ex) {// Destroy already created singletons to avoid dangling resources.// 出現(xiàn)異常銷毀beansdestroyBeans();// Reset 'active' flag.// 這個active在上面的prepare中被設置為了truecancelRefresh(ex);// Propagate exception to caller.throwex;? ? ? ? }? ? }}
接下來我們詳細來看一下容器的構建過程幢痘,在類AbstractRefreshableApplicationContext中
protectedConfigurableListableBeanFactoryobtainFreshBeanFactory(){// 刷新beanfactory唬格,這個方法很重要,就是它構建的bean,【繼續(xù)往里走】refreshBeanFactory();? ? ConfigurableListableBeanFactory beanFactory = getBeanFactory();if(logger.isDebugEnabled()) {? ? ? ? logger.debug("Bean factory for "+ getDisplayName() +": "+ beanFactory);? ? }returnbeanFactory;}
@OverrideprotectedfinalvoidrefreshBeanFactory()throwsBeansException{// 如果已經(jīng)存在beanfactory那就銷毀掉bean并把工廠關了颜说,避免對接下來的初始化造成影響if(hasBeanFactory()) {? ? ? ? destroyBeans();? ? ? ? closeBeanFactory();? ? }try{// 這里創(chuàng)建了一個DefaultListableBeanFactoryDefaultListableBeanFactory beanFactory = createBeanFactory();// 設置唯一id购岗,用于序列化beanFactory.setSerializationId(getId());// 自定義bean工廠customizeBeanFactory(beanFactory);// 向工廠中加載BeanDefinition,這個很重要门粪,【繼續(xù)往里走】loadBeanDefinitions(beanFactory);synchronized(this.beanFactoryMonitor) {this.beanFactory = beanFactory;? ? ? ? }? ? }catch(IOException ex) {thrownewApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex);? ? }}
loadBeanDefinitions在AbstractRefreshableApplicationContext中是個抽象方法喊积,我直接找到了在子類AbstractXmlApplicationContext(其實有三個實現(xiàn)類,但是我們現(xiàn)在研究的是FileSystemXmlApplicationContext)中的實現(xiàn)玄妈。
protectedvoidloadBeanDefinitions(DefaultListableBeanFactory beanFactory)throwsBeansException, IOException{// 看這名字乾吻,顯然就是用這家伙對來將xml配置文件中的信息讀取放到容器里的.XmlBeanDefinitionReader beanDefinitionReader =newXmlBeanDefinitionReader(beanFactory);//使用這個上下文的環(huán)境資源對這個reader進行配置beanDefinitionReader.setEnvironment(this.getEnvironment());? ? beanDefinitionReader.setResourceLoader(this);? ? beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));// 允許子類個性化對這個reader初始化initBeanDefinitionReader(beanDefinitionReader);// 然后開始真正的加載BeanDefinition,【繼續(xù)往里走】loadBeanDefinitions(beanDefinitionReader);}
這次是跳轉到AbstractXmlApplicationContext里面拟蜻,繼續(xù)閱讀
protectedvoidloadBeanDefinitions(XmlBeanDefinitionReader reader)throwsBeansException, IOException{// 獲取配置資源~~不過直接返回了一個null,不過有子類進行重寫的(如果看過HashMap源碼绎签,那肯定記得里面有幾個空實現(xiàn)是給LinkedHashMap用的,這里同樣的道理)Resource[] configResources = getConfigResources();if(configResources !=null) {? ? ? ? reader.loadBeanDefinitions(configResources);? ? }// 這個是類AbstractRefreshableConfigApplicationContext中的方法(跳來跳去腦殼都大了酝锅。诡必。。)// FileSystemXmlApplicationContext在refresh前就設置了String[] configLocations = getConfigLocations();if(configLocations !=null) {// 終于開始解析了搔扁,【繼續(xù)往里走】reader.loadBeanDefinitions(configLocations);? ? }}
AbstractBeanDefinitionReade中的方法爸舒,就是在這里進行加載的
@OverridepublicintloadBeanDefinitions(String... locations)throwsBeanDefinitionStoreException{? ? Assert.notNull(locations,"Location array must not be null");intcounter =0;for(String location : locations) {// ....【繼續(xù)往里面走】counter += loadBeanDefinitions(location);? ? }returncounter;}
還在本類里~不用跳了
publicintloadBeanDefinitions(String location, Set<Resource> actualResources)throwsBeanDefinitionStoreException{// 獲取資源加載器ResourceLoader resourceLoader = getResourceLoader();// 空就拋異常if(resourceLoader ==null) {thrownewBeanDefinitionStoreException("Cannot import bean definitions from location ["+ location +"]: no ResourceLoader available");? ? }// 這個是用來解析classpath*:這種的路徑蟋字,可以是多個配置文件if(resourceLoaderinstanceofResourcePatternResolver) {// Resource pattern matching available.try{//到這里getResource【完成了具體的定位】Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);// 開始載入BeanDefinitionintloadCount = loadBeanDefinitions(resources);if(actualResources !=null) {for(Resource resource : resources) {? ? ? ? ? ? ? ? ? ? actualResources.add(resource);? ? ? ? ? ? ? ? }? ? ? ? ? ? }if(logger.isDebugEnabled()) {? ? ? ? ? ? ? ? logger.debug("Loaded "+ loadCount +" bean definitions from location pattern ["+ location +"]");? ? ? ? ? ? }returnloadCount;? ? ? ? }catch(IOException ex) {thrownewBeanDefinitionStoreException("Could not resolve bean definition resource pattern ["+ location +"]", ex);? ? ? ? }? ? }else{// 到這里getResource,resource接口中封裝了很多與I/O相關的操作// 至此【完成了具體的定位】Resource resource = resourceLoader.getResource(location);// 開始載入BeanDefinition【繼續(xù)往里跳】intloadCount = loadBeanDefinitions(resource);if(actualResources !=null) {? ? ? ? ? ? actualResources.add(resource);? ? ? ? }if(logger.isDebugEnabled()) {? ? ? ? ? ? logger.debug("Loaded "+ loadCount +" bean definitions from location ["+ location +"]");? ? ? ? }returnloadCount;? ? }}
refresh方法完成IoC的整個初始化,其中 refreshBeanFactory()方法非常的重要扭勉,本小節(jié)講到定位鹊奖,下一小節(jié)開始講解BeanDefinition解析與加載。
BeanDefinition的載入和解析
對于IoC容器來說涂炎,這個載入過程相當于把xml中的BeanDefinition轉換成一個Spring內(nèi)部的數(shù)據(jù)結構的過程嫉入。IoC容器對Bean的管理和依賴注入功能是通過對其持有的BeanDefinition進行各種相關操作來實現(xiàn)的。這些BeanDefinition是通過一個HashMap來實現(xiàn)的璧尸。
承接上文,loadBeanDefinitions()是對BeanDefinition載入和解析的核心方法熬拒。具體實現(xiàn)在XMLBeanDefinitionReader里面爷光。
publicintloadBeanDefinitions(Resource... resources)throwsBeanDefinitionStoreException{? ? Assert.notNull(resources,"Resource array must not be null");intcounter =0;for(Resource resource : resources) {// 這里是循環(huán)加載,【繼續(xù)往里面跳】counter += loadBeanDefinitions(resource);? ? }returncounter;}
publicintloadBeanDefinitions(Resource resource)throwsBeanDefinitionStoreException{// 對Resource進行包裝,提供通過不同編碼方式讀取資源文件,【繼續(xù)往里面跳】returnloadBeanDefinitions(newEncodedResource(resource));}
publicintloadBeanDefinitions(EncodedResource encodedResource)throwsBeanDefinitionStoreException{? ? Assert.notNull(encodedResource,"EncodedResource must not be null");if(logger.isInfoEnabled()) {? ? ? ? logger.info("Loading XML bean definitions from "+ encodedResource.getResource());? ? }// 獲取已經(jīng)加載的ResourceSet currentResources =this.resourcesCurrentlyBeingLoaded.get();if(currentResources ==null) {? ? ? ? currentResources =newHashSet(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);? ? }// 解決重復依賴問題澎粟,encodedResource的equals方法已經(jīng)被重寫if(!currentResources.add(encodedResource)) {thrownewBeanDefinitionStoreException("Detected cyclic loading of "+ encodedResource +" - check your import definitions!");? ? }// 這里獲取IO準備讀取XML中的BeanDefinitiontry{? ? ? ? InputStream inputStream = encodedResource.getResource().getInputStream();try{? ? ? ? ? ? InputSource inputSource =newInputSource(inputStream);if(encodedResource.getEncoding() !=null) {? ? ? ? ? ? ? ? inputSource.setEncoding(encodedResource.getEncoding());? ? ? ? ? ? }// 【繼續(xù)往里面跳】returndoLoadBeanDefinitions(inputSource, encodedResource.getResource());? ? ? ? }finally{? ? ? ? ? ? inputStream.close();? ? ? ? }? ? }catch(IOException ex) {thrownewBeanDefinitionStoreException("IOException parsing XML document from "+ encodedResource.getResource(), ex);? ? }finally{? ? ? ? currentResources.remove(encodedResource);if(currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();? ? ? ? }? ? }}
這個是在XMLBeanDefinitionReader中實現(xiàn)(沒有進其他類)
protectedintdoLoadBeanDefinitions(InputSource inputSource, Resource resource)throwsBeanDefinitionStoreException{try{// 這里取得XML的document對象蛀序,解析由documentLoader完成,感興趣看以進去看看步驟// 雖然已經(jīng)對xml進行解析但是并沒有按照bean的規(guī)則活烙,所以需要繼續(xù)解析Document doc = doLoadDocument(inputSource, resource);// 這里啟動的是對beanDefinition解析的詳細過程徐裸,很重要的方法,【繼續(xù)往里面跳】returnregisterBeanDefinitions(doc, resource);? ? }catch(BeanDefinitionStoreException ex) {throwex;? ? }catch(SAXParseException ex) {thrownewXmlBeanDefinitionStoreException(resource.getDescription(),"Line "+ ex.getLineNumber() +" in XML document from "+ resource +" is invalid", ex);? ? }catch(SAXException ex) {thrownewXmlBeanDefinitionStoreException(resource.getDescription(),"XML document from "+ resource +" is invalid", ex);? ? }catch(ParserConfigurationException ex) {thrownewBeanDefinitionStoreException(resource.getDescription(),"Parser configuration exception parsing XML from "+ resource, ex);? ? }catch(IOException ex) {thrownewBeanDefinitionStoreException(resource.getDescription(),"IOException parsing XML document from "+ resource, ex);? ? }catch(Throwable ex) {thrownewBeanDefinitionStoreException(resource.getDescription(),"Unexpected exception parsing XML document from "+ resource, ex);? ? }}
仍然在本類中
publicintregisterBeanDefinitions(Document doc, Resource resource)throwsBeanDefinitionStoreException{// 創(chuàng)建BeanDefinitionDocumentReader對document進行解析BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();? ? documentReader.setEnvironment(this.getEnvironment());intcountBefore = getRegistry().getBeanDefinitionCount();// 具體的解析過程啸盏,【繼續(xù)往里面跳】documentReader.registerBeanDefinitions(doc, createReaderContext(resource));returngetRegistry().getBeanDefinitionCount() - countBefore;}
進入DefaultBeanDefinitionDocumentReader類中重贺,在文檔元素中獲取根元素,并繼續(xù)調(diào)用doRegisterBeanDefinition進行注冊回懦。
publicvoidregisterBeanDefinitions(Document doc, XmlReaderContext readerContext){this.readerContext = readerContext;? ? logger.debug("Loading bean definitions");? ? Element root = doc.getDocumentElement();// 【繼續(xù)往里面跳】doRegisterBeanDefinitions(root);}
protectedvoiddoRegisterBeanDefinitions(Element root){? ? String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if(StringUtils.hasText(profileSpec)) {? ? ? ? Assert.state(this.environment !=null,"Environment must be set for evaluating profiles");? ? ? ? String[] specifiedProfiles = StringUtils.tokenizeToStringArray(? ? ? ? ? ? profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if(!this.environment.acceptsProfiles(specifiedProfiles)) {return;? ? ? ? }? ? }? ? BeanDefinitionParserDelegate parent =this.delegate;this.delegate = createDelegate(this.readerContext, root, parent);? ? preProcessXml(root);//? 對BeanDefinition進行解析气笙,該方法的核心邏輯,【繼續(xù)往里面跳】parseBeanDefinitions(root,this.delegate);? ? postProcessXml(root);this.delegate = parent;}
// 不難看出怯晕,這是對xml的解析過程protectedvoidparseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate){if(delegate.isDefaultNamespace(root)) {? ? ? ? NodeList nl = root.getChildNodes();for(inti =0; i < nl.getLength(); i++) {? ? ? ? ? ? Node node = nl.item(i);if(nodeinstanceofElement) {? ? ? ? ? ? ? ? Element ele = (Element) node;if(delegate.isDefaultNamespace(ele)) {// 本邏輯為核心邏輯潜圃,【繼續(xù)往里面跳】parseDefaultElement(ele, delegate);? ? ? ? ? ? ? ? }else{? ? ? ? ? ? ? ? ? ? delegate.parseCustomElement(ele);? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }? ? }else{? ? ? ? delegate.parseCustomElement(root);? ? }}
privatevoidparseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate){// import標簽if(delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {? ? ? ? importBeanDefinitionResource(ele);? ? }// 別名elseif(delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {? ? ? ? processAliasRegistration(ele);? ? }// bean標簽elseif(delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {// 對bean標簽進行解析,【繼續(xù)往里面跳】processBeanDefinition(ele, delegate);? ? }// beans標簽elseif(delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);? ? }}
protectedvoidprocessBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate){// 這個是BeanDefinitionHolder里裝有BeanDefinition對象和beanname舟茶,別名集合等信息// parseBeanDefinitionElement()這個方法將xml中bean的定義進行解析谭期,有興趣可以進去深入了解BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if(bdHolder !=null) {? ? ? ? bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try{// 向IoC容器注冊解析到的BeanDefinition,【繼續(xù)往里面跳】BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());? ? ? ? }catch(BeanDefinitionStoreException ex) {? ? ? ? ? ? getReaderContext().error("Failed to register bean definition with name '"+? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bdHolder.getBeanName() +"'", ele, ex);? ? ? ? }// Send registration event.getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder));? ? }}
至此吧凉,XML中的BeanDefinition的解析和載入全部完成隧出,接下來進入bean的注冊部分。
BeanDefinition在IoC容器中的注冊
經(jīng)過定位和載入后客燕,BeanDefinition已經(jīng)在IoC建立起相應的數(shù)據(jù)結構鸳劳,為了更友好的使用這些BeanDefinition,需要在IoC容器中將這些BeanDefinition進行注冊也搓。
該方法在BeanDefinitionReaderUtils類中
publicstaticvoidregisterBeanDefinition(
? ? BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throwsBeanDefinitionStoreException{? ? String beanName = definitionHolder.getBeanName();// 這個很明顯是將BeanDefinition注冊的方法赏廓,【繼續(xù)往里面跳】registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// Register aliases for bean name, if any.String[] aliases = definitionHolder.getAliases();if(aliases !=null) {for(String aliase : aliases) {? ? ? ? ? ? registry.registerAlias(beanName, aliase);? ? ? ? }? ? }}
跳轉到DefaultListableBeanFactory類中涵紊,前面創(chuàng)建工廠時用的就是這個工廠
publicvoidregisterBeanDefinition(String beanName, BeanDefinition beanDefinition)throwsBeanDefinitionStoreException{? ? Assert.hasText(beanName,"Bean name must not be empty");? ? Assert.notNull(beanDefinition,"BeanDefinition must not be null");if(beanDefinitioninstanceofAbstractBeanDefinition) {try{? ? ? ? ? ? ((AbstractBeanDefinition) beanDefinition).validate();? ? ? ? }catch(BeanDefinitionValidationException ex) {thrownewBeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);? ? ? ? }? ? }// 為了保證數(shù)據(jù)一致性,注冊時加個synchronized線程鎖synchronized(this.beanDefinitionMap) {// 檢查在IoC容器中是否有同名bean幔摸,有同名的還不讓覆蓋的就是拋異常BeanDefinition oldBeanDefinition =this.beanDefinitionMap.get(beanName);if(oldBeanDefinition !=null) {if(!this.allowBeanDefinitionOverriding) {thrownewBeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Cannot register bean definition ["+ beanDefinition +"] for bean '"+ beanName +"': There is already ["+ oldBeanDefinition +"] bound.");? ? ? ? ? ? }elseif(oldBeanDefinition.getRole() < beanDefinition.getRole()) {if(this.logger.isWarnEnabled()) {this.logger.warn("Overriding user-defined bean definition for bean '"+ beanName +" with a framework-generated bean definition ': replacing ["+oldBeanDefinition +"] with ["+ beanDefinition +"]");? ? ? ? ? ? ? ? }? ? ? ? ? ? }else{if(this.logger.isInfoEnabled()) {this.logger.info("Overriding bean definition for bean '"+ beanName +"': replacing ["+ oldBeanDefinition +"] with ["+ beanDefinition +"]");? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }else{this.beanDefinitionNames.add(beanName);this.frozenBeanDefinitionNames =null;? ? ? ? }// 把BeanDefinition裝到如到beanDefinitionMap中// 【至此Spring IoC容器初始化完成~】// beanDeinitionMap是初始長度64的ConcurrentHashMapthis.beanDefinitionMap.put(beanName, beanDefinition);? ? }? ? resetBeanDefinition(beanName);}
到這里摸柄,注冊完成。我們創(chuàng)建bean工廠既忆,將BeanDefinition注冊到了IoC容器持有的Map中驱负。這些信息是控制反轉的基礎。
三患雇、小結
本文開始簡略解釋IoC的概念跃脊,然后從FileSystemXmlApplicationContext著手,根據(jù)源碼一步一步講述從bean工廠創(chuàng)建到BeanDefinition在IoC中注冊核心邏輯苛吱。Spring源碼確實細節(jié)太多酪术,在閱讀源碼過程中,一定要抓住核心邏輯翠储。
本文是博主在學習Spring源碼過程中對IoC的總結绘雁,希望對想要閱讀源碼但不知從何下手的同學有所幫助!如有錯誤希望大家指正援所。
庐舟!
在這里給大家提供一個學習交流的平臺,Java技術交流┟ 810309655
具有1-5工作經(jīng)驗的住拭,面對目前流行的技術不知從何下手挪略,需要突破技術瓶頸的可以加群。
在公司待久了废酷,過得很安逸瘟檩,但跳槽時面試碰壁。需要在短時間內(nèi)進修澈蟆、跳槽拿高薪的可以加群墨辛。
如果沒有工作經(jīng)驗,但基礎非常扎實趴俘,對java工作機制睹簇,常用設計思想,常用java開發(fā)框架掌握熟練的可以加群寥闪。