从小白的角度看Spring核心流程概览(XML版)第四章-BeanFactory加载资源文件

上一章看到了BeanFactory的创建过程,只剩下加载配置文件没有看了。现在我们接着this.loadBeanDefinitions(beanDefinitionReader);这行代码继续~

友情提示:方法观看顺序,从上往下看

1、加载配置文件

AbstractXmlApplicationContext类

	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        // 上次讲到这里,去加载配文件~
        this.loadBeanDefinitions(beanDefinitionReader);
    }
	//接着上面的代码
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        //xml版的Resource是为空的,所以这里不会有值。不信的话看下面那个方法。
        Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
		//获取ConfigLocations,这里只有ApplicationContext.xml
        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            //这里调用了AbstractBeanDefinitionReader的loadBeanDefinitions方法
            reader.loadBeanDefinitions(configLocations);
        }

    }

    protected Resource[] getConfigResources() {
        return null;
    }

AbstractBeanDefinitionReader类

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    Assert.notNull(locations, "Location array must not be null");
    int counter = 0;
    // 循环读取配置文件
    for (String location : locations) {
        counter += loadBeanDefinitions(location);
    }
    return counter;
}

//接着上面那个方法的~
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(location, null);
}

//接着上面那个方法的~
public int loadBeanDefinitions(String location, [@Nullable](https://my.oschina.net/u/2896689) Set<Resource> actualResources) throws BeanDefinitionStoreException {
    // 获取资源加载器
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException(
            "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    }
	// 这里判断了一下资源加载器的类型,但是不管怎么样,都会进loadBeanDefinitions(resources)方法
    if (resourceLoader instanceof ResourcePatternResolver) {
        // Resource pattern matching available.
        try {
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            int loadCount = 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 + "]");
            }
            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);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
        }
        return loadCount;
    }
}
// 接着上面那个方法
// 这是一个重载的方法,细心的已经发现和上面第一个方法是一样的,只是参数不一样。
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
    Assert.notNull(resources, "Resource array must not be null");
    int counter = 0;
    for (Resource resource : resources) {
        // 实际调用了XmlBeanDefinitionReader的loadBeanDefinitions方法
        counter += loadBeanDefinitions(resource);
    }
    return counter;
}

XmlBeanDefinitionReader类

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    // 这里把resource封装了下
    return loadBeanDefinitions(new EncodedResource(resource));
}

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);
    }
	// 看看当前线程中有没有在解析的EncodedResource,没有的话就保存进去,这里是没有的。
    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 {
            // 解析成xml的文件资源
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            // 真正将配置文件装换成beanDefinition的方法
            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();
        }
    }
}

//接着上面的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    throws BeanDefinitionStoreException {
    try {
        // 拿到xml的Document对象,这个大家应该很熟悉了把。。。
        Document doc = doLoadDocument(inputSource, resource);
        // 注册beanDefinition
        return registerBeanDefinitions(doc, resource);
    }
    catch (BeanDefinitionStoreException ex) {
        throw ex;
    }
    catch (SAXParseException ex) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                                                  "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    }
    catch (SAXException ex) {
        throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                                                  "XML document from " + resource + " is invalid", ex);
    }
    catch (ParserConfigurationException ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                                               "Parser configuration exception parsing XML from " + resource, ex);
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                                               "IOException parsing XML document from " + resource, ex);
    }
    catch (Throwable ex) {
        throw new BeanDefinitionStoreException(resource.getDescription(),
                                               "Unexpected exception parsing XML document from " + resource, ex);
    }
}
// 接着上面的方法
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    // 获取BeanDefinition文档解析器。
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    // 用文档解析器注册BeanDefinition
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

DefaultBeanDefinitionDocumentReader类

// 上一步创建的BeanDefinitionDocumentReader实际上是DefaultBeanDefinitionDocumentReader
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    // 保存一下xml文档
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    // 拿到root节点
    Element root = doc.getDocumentElement();
    // 真正注册的地方
    doRegisterBeanDefinitions(root);
}
// 接着上面的方法
protected void doRegisterBeanDefinitions(Element root) {
    // 先将当前的delegate保存起来,因为有可能是递归调用。
    BeanDefinitionParserDelegate parent = this.delegate;
    // 解析xml并创建Delegate,并保存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);
    // 真正将xml节点保存进BeanDefinition的方法,具体就是讲xml对应的属性放到BeanDefinition里面去,这里大家有兴趣的话可以点进去看(其实是太复杂了,笔者都绕晕了~)
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);

    this.delegate = parent;
}

2、BeanDefinition介绍

BeanDefinition保存的就是xml里面的所有配置项,也就是bean的定义,这里随意列举几个

public class AbstractBeanDefinition{
    // bean对象
	private volatile Object beanClass;
	// 作用域,默认单例,可以配成其他的
	private String scope = SCOPE_DEFAULT;
	// 是否抽象类
	private boolean abstractFlag = false;
	// 是否懒加载,默认不是
	private boolean lazyInit = false;
	// 自动加载策略
	private int autowireMode = AUTOWIRE_NO;
	// 初始化方法名
	private String initMethodName;
	// 销毁方法名
	private String destroyMethodName;
}

小结

Spring启动流程的资源定位和加载已经说完了,真的是各种调用(⊙o⊙)…还发现真正调用方法的地方都加了前缀do,果然是很规范~

转载于:https://my.oschina.net/u/3367603/blog/2208331

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值