Spring源码解析(三)——容器创建

前言

    承接上篇“容器刷新”,容器创建其实是“容器刷新”的一个子分支,但却涉及了许多内容,包括配置解析、IOC容器创建等。

 

源码解读

    让我们来回顾一下refresh的子分支之一,obtainFreshBeanFactory。

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        // 刷新 beanFactory,之前存在的话会先关闭再重新创建
        refreshBeanFactory();
        // 这一步其实就是获取上面这一步的结果
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

    主要的创建集中在refreshBeanFactory,由AbstractRefreshableApplicationContext实现,让我们展开来看。

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {

    @Override
    protected final void refreshBeanFactory() throws BeansException {
        // 如果之前初始化过 BeanFoctory,销毁并将成员变量置为 null
        if (hasBeanFactory()) {
            // 清空工厂已经注册的所有单例
            destroyBeans();
            // 将成员变量 beanFactory赋值 null
            closeBeanFactory();
        }
        try {
            /**
             * 创建 DefaultListableBeanFactory,此步只是创建了一个空壳
             * 如果存在父容器,将父容器 beanFactory传入构造器进行创建,这也是为什么父容器中 bean为什么对子容器可见的原因
             * 寻找 bean会优先从子容器查找,找不到才会从父容器查找
             */
            DefaultListableBeanFactory beanFactory = createBeanFactory();

            // 为序列化指定id,其实将 id与一个 指向该工厂的弱引用关联在一起
            beanFactory.setSerializationId(getId());

            // 定制 beanFactory
            customizeBeanFactory(beanFactory);

            // 加载 BeanDefinition
            loadBeanDefinitions(beanFactory);

            // 赋值给成员变量 beanFactory
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

    protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }

    /**
     * 如果它实现ConfigurableApplicationContext,则返回父上下文的内部 Bean工厂;
     * 否则,返回父上下文本身
     */
    protected BeanFactory getInternalParentBeanFactory() {
        return (getParent() instanceof ConfigurableApplicationContext) ?
                ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
    }

    // 设置:1.同名对象覆盖;2.循环依赖
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
            throws BeansException, IOException;
}

       AbstractRefreshableApplicationContext只是把架子给搭建起来了,创建了一个空壳子DefaultListableBeanFactory,具体的填充工作是loadBeanDefinitions。由第一篇容器启动可知,默认使用的ApplicationContext是XmlWebApplicationContext,让我们继续看代码。

public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 创建 XmlBeanDefinitionReader 以解析配置文件
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // 配置 XmlBeanDefinitionReader
        beanDefinitionReader.setEnvironment(getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // 空实现:留给子类扩展
        initBeanDefinitionReader(beanDefinitionReader);
        // 调用 reader的 loadBeanDefinitions
        loadBeanDefinitions(beanDefinitionReader);
    }

    // 留给子类自定义扩展 XmlBeanDefinitionReader
    protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
    }

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        // 获取配置的文件路径
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            // 遍历解析配置文件
            for (String configLocation : configLocations) {
                reader.loadBeanDefinitions(configLocation);
            }
        }
    }
}

    遍历配置文件路径数组解析,也就是我们在web.xml中配置的<init-param> contextConfigLocation(见第一篇),之后的工作就看XmlBeanDefinitionReader的了,如果之前研究过Spring源码的朋友可能有印象,XmlBeanFactory的构造器就是调用了XmlBeanDefinitionReader来解析配置文件的,不过在Spring 3.1版本之后,XmlBeanFactory被标记为废弃了。

 

配置解析
public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader {

    @Override
    public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(location, null);
    }

    public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        }

        if (resourceLoader instanceof ResourcePatternResolver) {
            try {
                // 支持“*”通配符匹配,例如:spring-mybatis.xml、spring-mvc.xml,配置成 spring-*.xml即可
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                int loadCount = loadBeanDefinitions(resources);
                if (actualResources != null) {
                    for (Resource resource : resources) {
                        actualResources.add(resource);
                    }
                }
				......// 省略日志
                return loadCount;
            } catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        } else {
            // 只能通过绝对路径加载单个资源.
            Resource resource = resourceLoader.getResource(location);
            int loadCount = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
			......// 省略日志
            return loadCount;
        }
    }

    @Override
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        for (Resource resource : resources) {
            // 这里由子类实现:XmlBeanDefinitionReader
            counter += loadBeanDefinitions(resource);
        }
        return counter;
    }

    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
}

    抽象父类同样是搭架子,主要工作就是把原本String类型的路径(支持通配符 *),解析为Resource

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        ......// 省略断言和日志

        // 往 ThreadLocal放一个正在加载的 Resource
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<EncodedResource>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        // 此步为了防止循环加载,Set机制:add相同的元素返回 false
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }

        try {
            // 获取输入流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                // 使用 SAX解析配置文件
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            } finally {
                // 关闭 IO流(一定要做好资源的清理、关闭工作)
                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();
            }
        }
    }

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
            // 将 xml文件封装成 Document对象
            Document doc = doLoadDocument(inputSource, resource);
            // 注册 BeanDefinition
            return registerBeanDefinitions(doc, resource);
        }
		.....// 省略catch处理
    }

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        /**
         * 默认实现类:DefaultBeanDefinitionDocumentReader
         * 对<beans>、<alias>、<import>、<resource>等标签的解析
         */
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        // 获取注册前已经注册的 BeanDefinition数量
        int countBefore = getRegistry().getBeanDefinitionCount();

        // 对 Document对象的解析——> BeanDefinition注册
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

        // 作差获取本次解析注册的 BeanDefinition数量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
}

    到这里位置,XmlBeanDefinitionReader的工作基本也告于段落了,主要就是把Resource类型,通过IO流处理封装成 Document对象。其实我们从命名当中就可以看出各自的作用,顺理成章,接下来就是BeanDefinitionDocumentReader的功能介绍了。

 

总结

    由于接下来主要是对xml中的标签进行解析,主要包含<beans>、<bean>、<import>、<alias>,内容也算比较多,所以另算章节进行展开解读。

    回头看本篇,内容相对来说还是很简单,主要就是“空壳”工厂的创建、配置文件的解析,我们最关心的Bean解析注册将放在下节讲解。

转载于:https://my.oschina.net/marvelcode/blog/1829849

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值