小编教您Spring源码分析之IoC容器初始化

作为一个java程序员,保守估计一年里也都有300天要和Spring有亲密接触~~像我这种怕是每天都要撸撸Spring,所以这次也要做个深入了解!这次就来看看Spring是怎么初始化IoC容器的😄

注:阅读本文时一定要在IDE进行跳转

我们都是知道Spring为企业级应用提供了丰富的功能,而这些功能底层都依赖于底层最核心的两种特性IOC和AOP。

IOC实现里主要包括两部分,一个是IOC容器初始化,另外一个是依赖注入,由于两部分是相对独立的部分,所以分成不同文章讲解,本篇主要讲述IOC容器的初始化

一、IoC的概念

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

上面这个概念是来自维基百科,在Expert Spring MVC and Web FlowExpert One-on-One J2EE without EJB等书中都也是将依赖注入看作是IoC的一种方式。不过有些地方会把IoC和DI看成一个概念(例如Spring in Action、Spring揭密等书),不过没关系,不影响我们理解就可以。

白话版

A类的正常的运行需要B类

没有IoC之前我们在使用A类的某个方法时,总会依赖于B类的一些功能,这样我们就要去new个B对象,有时还要考虑什么时候销毁,什么时候使用单例模式等等,类少了还好管理,这要是多起来真是再聪明的人怕也是要十个头九个大了, 而且A、B之间的依赖关系使各代码紧密耦合,一旦B类的出现问题,或者某天干脆不用B了用C,那是不是A类里的new B()全得换成new C()?想象都觉得累...

有了IoC之后,对象创建都交给第三方容器处理,A中的B通过注入来完成,B出问题了,或者需要换成C了,只要把注入的B换成C就可以(现实开发中B、C可以实现相同的接口解决~所以啊,Spring是面向接口编程鸭)。

Tips

Expert One-on-One J2EE without EJB这本书是spring爸爸Rod Johnson写的,进入Spring的BeanFactory类里面看看作者就是他,哈哈!

浅谈控制反转与依赖注入:这是我看过最好的一篇对控制反转的解释,强烈推荐!

二、IoC容器初始化

预备内容

本节只讲解IoC容器的初始化,其中包括创建容器和将bean装入到容器中,下面这三件事是该部分的核心:

BeanDefinition的Resource定位

BeanDefinition的载入和解析

BeanDefinition在容器中的注册

因为Spring的IoC容器实现太复杂了,各种类之间的调用很容易就让我们陷入到细节之中,结果就走的太远忘记了为啥要出发了😓,本文主要将述容器初始化时最主要的三件事。

先了解几个概念:

BeanFactory:这是IOC容器的接口定义,提供了IoC最基本的功能,如果说容器是个汽车工厂,那么这个结构就规定了汽车工厂最基本的功能,能储存零件,能组装汽车。

publicinterfaceBeanFactory{/**

    * 使用容器获取bean时,添加转义自符&可以获取到FactoryBean本身而吧是Factory产生的对象

    */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;/**

    * 通过名字和参数来获取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的定义,是让容器起作用的主要数据类型。对 IOC 容器来说,BeanDefinition 就是对控制反转模式中管理的对象依赖关系的数据抽象。

接下来正式进入IoC容器初始化的分析,以FileSystemXmlApplicationContext为例,下面是FileSystemXmlApplicationContext的继承关系~(这形状,满满的爱啊,哈哈)

BeanDefinition的Resource定位

publicclassFileSystemXmlApplicationContextextendsAbstractXmlApplicationContext{/**

    * 无参构造

    */publicFileSystemXmlApplicationContext(){    }/**

    * 传入父类上下文

    */publicFileSystemXmlApplicationContext(ApplicationContext parent){super(parent);    }/**

    * 核心构造方法,其他几个都基于本构造方法

    * configLocations 传入xml配置文件位置集合

    * refresh 是否自动刷新容器(是refresh方法的调用,初始化上下文的核心方法)

    * parent 父类上下文

    * 1.传入配置文件地址

    * 2.刷新容器

    */publicFileSystemXmlApplicationContext(String[] configLocations,booleanrefresh, ApplicationContext parent)throwsBeansException{super(parent);        setConfigLocations(configLocations);if(refresh) {            refresh();        }    }/**

    * 通过给定的路径在文件系统中定位BeanDefinition并返回一个FileSystemResource

    * 这个方法是BeanDefinitionReader的loadBeanDefinition中被调用,

    * loadBeanDefinition采用了模板模式,具体实现在不同的子类中(默认是类路径)

    */@OverrideprotectedResourcegetResourceByPath(String path){if(path !=null&& path.startsWith("/")) {            path = path.substring(1);        }returnnewFileSystemResource(path);    }}

看以看出,本类对所有configLocation都进行了处理,使所有以xml形式存在的BeanDefinition都得到了处理,其中这个refresh就最最关键点方法,接下来对refresh进行解析。

refresh是在AbstractApplicationContext中实现,理解了refresh方法,基本就理解了IoC初始化的全过程了。

publicvoidrefresh()throwsBeansException, IllegalStateException{synchronized(this.startupShutdownMonitor) {// 刷新前准备活动prepareRefresh();// 关键方法构建beanFactory——>接下来会详解本方法ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 为在这个上下文中使用beanFactory做准备prepareBeanFactory(beanFactory);try{// 设置后置处理器postProcessBeanFactory(beanFactory);// 调用bean的后置处理器,这些处理器在上下文中被注册为bean的形式// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.// 注册拦截bean创建的处理器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.// 检查监听的bean,并将他们注册到容器中registerListeners();// Instantiate all remaining (non-lazy-init) singletons.// 初始化所有的非懒加载单件finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.// 发布相关事件,结束refreshfinishRefresh();        }catch(BeansException ex) {// Destroy already created singletons to avoid dangling resources.// 出现异常销毁beansdestroyBeans();// Reset 'active' flag.// 这个active在上面的prepare中被设置为了truecancelRefresh(ex);// Propagate exception to caller.throwex;        }    }}

接下来我们详细来看一下容器的构建过程,在类AbstractRefreshableApplicationContext中

protectedConfigurableListableBeanFactoryobtainFreshBeanFactory(){// 刷新beanfactory,这个方法很重要,就是它构建的bean,【继续往里走】refreshBeanFactory();    ConfigurableListableBeanFactory beanFactory = getBeanFactory();if(logger.isDebugEnabled()) {        logger.debug("Bean factory for "+ getDisplayName() +": "+ beanFactory);    }returnbeanFactory;}

@OverrideprotectedfinalvoidrefreshBeanFactory()throwsBeansException{// 如果已经存在beanfactory那就销毁掉bean并把工厂关了,避免对接下来的初始化造成影响if(hasBeanFactory()) {        destroyBeans();        closeBeanFactory();    }try{// 这里创建了一个DefaultListableBeanFactoryDefaultListableBeanFactory beanFactory = createBeanFactory();// 设置唯一id,用于序列化beanFactory.setSerializationId(getId());// 自定义bean工厂customizeBeanFactory(beanFactory);// 向工厂中加载BeanDefinition,这个很重要,【继续往里走】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(其实有三个实现类,但是我们现在研究的是FileSystemXmlApplicationContext)中的实现。

protectedvoidloadBeanDefinitions(DefaultListableBeanFactory beanFactory)throwsBeansException, IOException{// 看这名字,显然就是用这家伙对来将xml配置文件中的信息读取放到容器里的.XmlBeanDefinitionReader beanDefinitionReader =newXmlBeanDefinitionReader(beanFactory);//使用这个上下文的环境资源对这个reader进行配置beanDefinitionReader.setEnvironment(this.getEnvironment());    beanDefinitionReader.setResourceLoader(this);    beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));// 允许子类个性化对这个reader初始化initBeanDefinitionReader(beanDefinitionReader);// 然后开始真正的加载BeanDefinition,【继续往里走】loadBeanDefinitions(beanDefinitionReader);}

这次是跳转到AbstractXmlApplicationContext里面,继续阅读

protectedvoidloadBeanDefinitions(XmlBeanDefinitionReader reader)throwsBeansException, IOException{// 获取配置资源~~不过直接返回了一个null,不过有子类进行重写的(如果看过HashMap源码,那肯定记得里面有几个空实现是给LinkedHashMap用的,这里同样的道理)Resource[] configResources = getConfigResources();if(configResources !=null) {        reader.loadBeanDefinitions(configResources);    }// 这个是类AbstractRefreshableConfigApplicationContext中的方法(跳来跳去脑壳都大了。。。)// FileSystemXmlApplicationContext在refresh前就设置了String[] configLocations = getConfigLocations();if(configLocations !=null) {// 终于开始解析了,【继续往里走】reader.loadBeanDefinitions(configLocations);    }}

AbstractBeanDefinitionReade中的方法,就是在这里进行加载的

@OverridepublicintloadBeanDefinitions(String... locations)throwsBeanDefinitionStoreException{    Assert.notNull(locations,"Location array must not be null");intcounter =0;for(String location : locations) {// ....【继续往里面走】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【继续往里跳】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()方法非常的重要,本小节讲到定位,下一小节开始讲解BeanDefinition解析与加载。

BeanDefinition的载入和解析

对于IoC容器来说,这个载入过程相当于把xml中的BeanDefinition转换成一个Spring内部的数据结构的过程。IoC容器对Bean的管理和依赖注入功能是通过对其持有的BeanDefinition进行各种相关操作来实现的。这些BeanDefinition是通过一个HashMap来实现的。

承接上文,loadBeanDefinitions()是对BeanDefinition载入和解析的核心方法。具体实现在XMLBeanDefinitionReader里面。

publicintloadBeanDefinitions(Resource... resources)throwsBeanDefinitionStoreException{    Assert.notNull(resources,"Resource array must not be null");intcounter =0;for(Resource resource : resources) {// 这里是循环加载,【继续往里面跳】counter += loadBeanDefinitions(resource);    }returncounter;}

publicintloadBeanDefinitions(Resource resource)throwsBeanDefinitionStoreException{// 对Resource进行包装,提供通过不同编码方式读取资源文件,【继续往里面跳】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());    }// 获取已经加载的ResourceSet currentResources =this.resourcesCurrentlyBeingLoaded.get();if(currentResources ==null) {        currentResources =newHashSet(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);    }// 解决重复依赖问题,encodedResource的equals方法已经被重写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());            }// 【继续往里面跳】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中实现(没有进其他类)

protectedintdoLoadBeanDefinitions(InputSource inputSource, Resource resource)throwsBeanDefinitionStoreException{try{// 这里取得XML的document对象,解析由documentLoader完成,感兴趣看以进去看看步骤// 虽然已经对xml进行解析但是并没有按照bean的规则,所以需要继续解析Document doc = doLoadDocument(inputSource, resource);// 这里启动的是对beanDefinition解析的详细过程,很重要的方法,【继续往里面跳】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{// 创建BeanDefinitionDocumentReader对document进行解析BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();    documentReader.setEnvironment(this.getEnvironment());intcountBefore = getRegistry().getBeanDefinitionCount();// 具体的解析过程,【继续往里面跳】documentReader.registerBeanDefinitions(doc, createReaderContext(resource));returngetRegistry().getBeanDefinitionCount() - countBefore;}

进入DefaultBeanDefinitionDocumentReader类中,在文档元素中获取根元素,并继续调用doRegisterBeanDefinition进行注册。

publicvoidregisterBeanDefinitions(Document doc, XmlReaderContext readerContext){this.readerContext = readerContext;    logger.debug("Loading bean definitions");    Element root = doc.getDocumentElement();// 【继续往里面跳】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进行解析,该方法的核心逻辑,【继续往里面跳】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)) {// 本逻辑为核心逻辑,【继续往里面跳】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标签进行解析,【继续往里面跳】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,【继续往里面跳】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容器中的注册

经过定位和载入后,BeanDefinition已经在IoC建立起相应的数据结构,为了更友好的使用这些BeanDefinition,需要在IoC容器中将这些BeanDefinition进行注册。

该方法在BeanDefinitionReaderUtils类中

publicstaticvoidregisterBeanDefinition(

    BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throwsBeanDefinitionStoreException{    String beanName = definitionHolder.getBeanName();// 这个很明显是将BeanDefinition注册的方法,【继续往里面跳】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类中,前面创建工厂时用的就是这个工厂

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);        }    }// 为了保证数据一致性,注册时加个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);}

到这里,注册完成。我们创建bean工厂,将BeanDefinition注册到了IoC容器持有的Map中。这些信息是控制反转的基础。

三、小结

本文开始简略解释IoC的概念,然后从FileSystemXmlApplicationContext着手,根据源码一步一步讲述从bean工厂创建到BeanDefinition在IoC中注册核心逻辑。Spring源码确实细节太多,在阅读源码过程中,一定要抓住核心逻辑。

本文是博主在学习Spring源码过程中对IoC的总结,希望对想要阅读源码但不知从何下手的同学有所帮助!如有错误希望大家指正。

在这里给大家提供一个学习交流的平台,Java技术交流┟ 810309655

具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加群。

在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加群。

如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的可以加群。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 在上一篇文章,我们讲了 IoC 容器初始化的准备阶段,即找到 BeanDefinition 的 Resource ...
    偷星辰夜阅读 1,264评论 0 3
  • 作为Java程序员,Spirng我们再熟悉不过,可以说比自己的女朋友还要亲密,每天都会和他在一起,然而我们真的了解...
    莫那一鲁道阅读 15,464评论 7 58
  • IOC的基础 下面我们从IOC/AOP开始,它们是Spring平台实现的核心部分;虽然,我们一开始大多只是在这个层...
    011b8ee4cba4阅读 921评论 2 2
  • 今天老师让我们背诵一首诗,名字是《春晓》,作者是唐朝诗人孟浩然,他和李白、李峤、骆宾王和李绅是一个朝代的。...
    科幻LZY阅读 1,060评论 0 1
  • 有一种高效的学习方法叫费曼技巧。它的理论是教是最好的学。最近很想学好演讲。所以,今天来写一篇关于演讲的文章。准确一...
    学而时行之阅读 664评论 2 5