Spring Ioc 源码学习笔记

Spring Ioc 源码学习笔记

概述

Spring 里面最重要的特性就是 Ioc,可能你还会说 aop。其实 aop 的实现也是基于 ioc。Ioc (Inversion of Control),即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

下面具体看下spring如何加载bean,初始化bean的。

源码分析

我们先入口看一下:

public ClassPathXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {

        super(parent);
        //设置配置文件路径
        setConfigLocations(configLocations);
        //核心部分
        if (refresh) {
            refresh();
        }
    }

再具体看下refresh()的核心代码:

@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            prepareBeanFactory(beanFactory);
            try {
                postProcessBeanFactory(beanFactory);
                invokeBeanFactoryPostProcessors(beanFactory);
                registerBeanPostProcessors(beanFactory);
                initMessageSource();
                initApplicationEventMulticaster();
                onRefresh();
                registerListeners();
                finishBeanFactoryInitialization(beanFactory);
                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();
                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();
            }
        }
    }

下面具体分析一下:

prepareRefresh()

准备上下文的刷新,主要设置启动时间、激活和关闭的标志,以及应用事件监听集合的初始化,此处比较简单就不具体展开了,具体源码位置:AbstractApplicationContext—>prepareRefresh().

obtainFreshBeanFactory()

该方法的作用是:创建BeanFactory实例,并解析Spring的xml配置文件,放入beanDefinitionMap中,对于的key为beanName ,value 则是beanDefinition。

看看几个关键的函数:

  • refreshBeanFactory()

    @Override
    protected final void refreshBeanFactory() throws BeansException {
     //销毁已经存在的Beans,并关掉BeanFactory
     if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
     }
     try {
        //创建beanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        //设置重复name覆盖和循环引用
        customizeBeanFactory(beanFactory);
        //加载bean到beanFactory中
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
           this.beanFactory = beanFactory;
        }
     }
     catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
     }
    }

这里要注意的是customizeBeanFactorys设置覆盖的问题,就是在配置文件中定义 bean 时使用了相同的 id 或 name,默认情况下,allowBeanDefinitionOverriding 属性为 null,如果在同一配置文件中重复了,会抛错,但是如果不是同一配置文件中,会发生覆盖。

  • loadBeanDefinitions(beanFactory)

    扫描二维码关注公众号,回复: 1371306 查看本文章

    此处主要实例化一个XmlBeanDefinitionReader对象来为后面解析xml,装载bean做准备

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    //为beanFactory初始化一个XmlBeanDefinitionReader主要负责加载配置、解析。
    //这里可以看出来ApplicationContext不负责解析xml,而是委托XmlBeanDefinitionReader来解析
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // 设置beanDefinition参数
    // 加载环境参数
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // 初始化beanDefinitionReader 
    initBeanDefinitionReader(beanDefinitionReader);
    //beanDefinition读取
    loadBeanDefinitions(beanDefinitionReader);
}

在具体看loadBeanDefinitions(beanDefinitionReader);之前,我们先看下BeanDefinition这个接口的一些方法

  • BeanDefinition.java

    BeanDefinition接口定义详解

  • 看完beanDefinition之后,我们继续看如何加载bean的定义loadBeanDefinitions(beanDefinitionReader);

    注意一下,我这里省去了中间的一部分代码,这个看一下就好了,直接看核心部分。

    还需要说明一下的是:这里的Resource其实是怎么来的,其实是从configLocations(这个大家要记得在最开始初始化的时候 setConfigLocations(configLocations)的时候设置的值),转成Resourced的,核心代码为:

    Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);

    怕混淆,注明一下这个方法的路径:XmlBeanDefinitionReader–>loadBeanDefinitions(EncodedResource encodedResource)

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());
            }
            //前面都是一些变量的初始化,以及将encodedResource放入到resourcesCurrentlyBeingLoaded这个ThreadLocal变量中
            //这里是具体的读取过程    
            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();
        }
    }
}

继续往下看doLoadBeanDefinitions这个方法,省去了try{}catch

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
        //将inputstream流读取到doc
        Document doc = doLoadDocument(inputSource, resource);
        //bean的注册
        return registerBeanDefinitions(doc, resource);
    }
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        //继续
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //返回bean的数量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

registerBeanDefinitions方法主要是获取Element元素,其主要是doRegisterBeanDefinitions方法

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 用于解析XML bean定义的有状态委托类。
        BeanDefinitionParserDelegate parent = this.delegate;
        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);
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

未完待续。。。

猜你喜欢

转载自blog.csdn.net/wagnteng/article/details/80222113