Spring源码分析系列(一)IOC容器的设计与实现(1)基础容器的实现

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

ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

context.getBean("helloword");

         简单分析这段代码,通过接口ApplicationContext初始化了该结构的实现类,ClassPathXmlAppliction并调用构造方法,传入一个classpath地址去读取。我们以applicationContext为起点,看他上下相关的类。applicationContext接口的最终父类是BeanFactory,他定义了我们IOC容器的基本特征。

1.1

    那么BeanFactory与ApplicaionContext的关系是什么呢?我们可以说BeanFactory是我们定义IOC容器的基础形式,而ApplicationContext则是我们定义容器的高级形式。让我们先从基础IOC开始分析。看图1.2BeanFactory实现的最底层是xmlBeanFactory,仔细读xmlBeanFactory的源码可以看出,xmlBeanFactory是DefaultListableBeanFactory的扩展,在DefaultListableBeanFactory的基础上增加了对xml文件解析的支持。

1.2

        上xmlBeanFactory源码

    public class XmlBeanFactory extends DefaultListableBeanFactory {

        private final XmlBeanDefinitionReader reader; public XmlBeanFactory(Resource resource) throws BeansException {             this(resource, (BeanFactory)null);

           }

        public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {             super(parentBeanFactory);

            this.reader = new XmlBeanDefinitionReader(this);

            this.reader.loadBeanDefinitions(resource);

        }

}

我们可以看到xmlBeanFactory的构造函数:构造父类,初始化XmlBeanDefinitionReader,执行loadBeanDefinitions。我们将这段代码提炼出来。

ClassPathResource resource = new ClassPathResource("bean.xml");

DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);

reader.loadBeanDefinitions(resource);    

上述代码可以解读为:

(1)Resouce定位发现资源bean的定义资源

(2)bean定义资源的载入

(3)bean定义资源向ioc容器的注册

我们逐步解析代码,首先从XmlBeanDefinitionReader 的初始化开始,加载Resource路径下的文件并转化为流

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null");

if (this.logger.isTraceEnabled()) {

this.logger.trace("Loading XML bean definitions from " + encodedResource);

}

Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();

if (currentResources == null) {

currentResources = new HashSet(4);

this.resourcesCurrentlyBeingLoaded.set(currentResources);

}

if (!((Set)currentResources).add(encodedResource)) {

throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");

} else {

int var5;

try {InputStream inputStream = encodedResource.getResource().getInputStream();

try { InputSource inputSource = new InputSource(inputStream);

if (encodedResource.getEncoding() != null) {

inputSource.setEncoding(encodedResource.getEncoding());

}

var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());

} finally { inputStream.close();    

}}

catch (IOException var15) {

throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);

}finally {

((Set)currentResources).remove(encodedResource);

if (((Set)currentResources).isEmpty()) {

this.resourcesCurrentlyBeingLoaded.remove();

}}return var5;

}}   

转流后通过调用doLoadBeanDefinitions方法取得xml文件的Document对象,然后开始进行详细的解析过程这个解析过程在DefaultBeanDefinitionDocumentReader.registerBeanDefinitions中完成。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { 

Document doc = this.doLoadDocument(inputSource, resource);

int count = this.registerBeanDefinitions(doc, resource);

if (this.logger.isDebugEnabled()) {

this.logger.debug("Loaded " + count + " bean definitions from " + resource);

} return count;

}

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {

BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();

int countBefore = this.getRegistry().getBeanDefinitionCount();

documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));

return this.getRegistry().getBeanDefinitionCount() - countBefore;

}

        关于xml节点的详细解析我们这里就不做一一列举了,主要看bean,bean的解析主要在processBeanDefinition方法中,这里定义了一个BeanDefinitionHolder 对象用来完成对IOC容器的注册,BeanDefinitionHolder本质上是 BeanDefinition的封装类,通过decorateBeanDefinitionIfRequired返回的对象完成BeanDefinitionHolder类的构造。

public BeanDefinitionHolder(BeanDefinitionHolder beanDefinitionHolder) {

Assert.notNull(beanDefinitionHolder, "BeanDefinitionHolder must not be null");

this.beanDefinition = beanDefinitionHolder.getBeanDefinition();

this.beanName = beanDefinitionHolder.getBeanName();

this.aliases = beanDefinitionHolder.getAliases();

}

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

if (bdHolder != null) {

bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

try {

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

} catch (BeanDefinitionStoreException var5) {

this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);

}

this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

}}


最终通过BeanDefinitionReaderUtils.registerBeanDefinition方法,开始注册,这里注册会根据不同的IOC容器来选择到底在哪里进行注册。我们是从DefaultListableBeanFactory文件初始化过来的,所以我们选它。


看registerBeanDefinition的实现代码,通过一系列的判断校验等,最终会将传进来的BeanName及初始Bean放入到一个

Map<String,BeanDefinition> beanDefinitionMap =new ConcurrentHashMap(256);的map结构中。

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty");

Assert.notNull(beanDefinition, "BeanDefinition must not be null");

if (beanDefinition instanceof AbstractBeanDefinition) {

try{

((AbstractBeanDefinition)beanDefinition).validate();

} catch (BeanDefinitionValidationException var8) {

throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var8);

} }

BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);

if (existingDefinition != null) {

if (!this.isAllowBeanDefinitionOverriding()) {

throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);

}

if (existingDefinition.getRole() < beanDefinition.getRole()) {

if (this.logger.isInfoEnabled()) {

this.logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");

} } else if (!beanDefinition.equals(existingDefinition)) {

if (this.logger.isDebugEnabled()) {

this.logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");

} } else if (this.logger.isTraceEnabled()) {

this.logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]");

} this.beanDefinitionMap.put(beanName, beanDefinition);

} else {

if (this.hasBeanCreationStarted()) {

synchronized(this.beanDefinitionMap) {

this.beanDefinitionMap.put(beanName, beanDefinition);

List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames);

updatedDefinitions.add(beanName);

this.beanDefinitionNames = updatedDefinitions;

this.removeManualSingletonName(beanName);

} } else { this.beanDefinitionMap.put(beanName, beanDefinition);

this.beanDefinitionNames.add(beanName);

this.removeManualSingletonName(beanName);

} this.frozenBeanDefinitionNames = null;

} if (existingDefinition != null || this.containsSingleton(beanName)) {

this.resetBeanDefinition(beanName);

}}

初始bean我们已经完成注入了,接下来我们将在IOC容器中建立依赖关系。首先我们通过getBean方法去向容器索要Bean,这里我们调用DefaultListableBeanFactory中的getBean。

ClassPathResource resource =new ClassPathResource("bean.xml");

DefaultListableBeanFactory factory =new DefaultListableBeanFactory();

XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);

reader.loadBeanDefinitions(resource);

factory.getBean("helloword");

        由于我们前文中在DefaultListableBeanFactory先执行了初始化,我们通过super()逐层向上抛,完成AbstractAutowire CapableBeanFactory、AbstractBeanFactory、BeanFactory等类的初始化。此时我们在DefaultListableBeanFactory中调用getBean,先会在父类AbstractBeanFactory中执行。等待一系列判断验重后调用createBean方法跳转到子类AbstractAutowireCapableBeanFactory中。


在执行createBean时,也需要判断传入BeanName是否可以实例化,是否可以反射等。最后通过校验调用doCreateBean,我们会初始化一个BeanWrapper 对象,这是我们真正的Bean的数据结构然后校验出多种情况进行处理。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {

BeanWrapper instanceWrapper =null;

    if (mbd.isSingleton()) {

instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);

    }

if (instanceWrapper ==null) {

instanceWrapper =this.createBeanInstance(beanName, mbd, args);

    }

Object bean = instanceWrapper.getWrappedInstance();

    Class beanType = instanceWrapper.getWrappedClass();

    if (beanType != NullBean.class) {

mbd.resolvedTargetType = beanType;

    }

synchronized(mbd.postProcessingLock) {

if (!mbd.postProcessed) {

try {

this.applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);

            }catch (Throwable var17) {

throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", var17);

            }

mbd.postProcessed =true;

        }

}

boolean earlySingletonExposure = mbd.isSingleton() &&this.allowCircularReferences &&this.isSingletonCurrentlyInCreation(beanName);

    if (earlySingletonExposure) {

if (this.logger.isTraceEnabled()) {

this.logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");

        }

this.addSingletonFactory(beanName, () -> {

return this.getEarlyBeanReference(beanName, mbd, bean);

        });

    }

Object exposedObject = bean;

    try {

this.populateBean(beanName, mbd, instanceWrapper);

        exposedObject =this.initializeBean(beanName, exposedObject, mbd);

    }catch (Throwable var18) {

if (var18instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {

throw (BeanCreationException)var18;

        }

throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);

    }

if (earlySingletonExposure) {

Object earlySingletonReference =this.getSingleton(beanName, false);

        if (earlySingletonReference !=null) {

if (exposedObject == bean) {

exposedObject = earlySingletonReference;

            }else if (!this.allowRawInjectionDespiteWrapping &&this.hasDependentBean(beanName)) {

String[] dependentBeans =this.getDependentBeans(beanName);

                Set actualDependentBeans =new LinkedHashSet(dependentBeans.length);

                String[] var12 = dependentBeans;

                int var13 = dependentBeans.length;

                for(int var14 =0; var14 < var13; ++var14) {

String dependentBean = var12[var14];

                    if (!this.removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {

actualDependentBeans.add(dependentBean);

                    }

}

if (!actualDependentBeans.isEmpty()) {

throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName +"' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");

                }

}

}

}

try {

this.registerDisposableBeanIfNecessary(beanName, bean, mbd);

        return exposedObject;

    }catch (BeanDefinitionValidationException var16) {

throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);

    }

}

这样就完成了我们对bean属性的依赖注入过程,最终我们那些具有依赖关系的Bean都会放在一个ConcurrentMap<String, BeanWrapper> map = currentHashMap();中。

一个最基础的IOC容器完成了依赖注入。



搞了个技术交流群,不定期的分享技术、划水、接私活什么的。大家有兴趣可以加下这是审核人的微信。


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