spring5源码分析系列(四)——IOC容器的初始化(二)

前言:上一篇讲到了Xml Bean读取器(XmlBeanDefinitionReader)调用其父类AbstractBeanDefinitionReader的reader.loadBeanDefinitions方法读取Bean定义资源,此篇我们继续后面的内容。

(5)AbstractBeanDefinitionReader的loadBeanDefinitions方法

方法源码如下:

//重载方法,调用下面的loadBeanDefinitions(String, Set<Resource>)方法
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(location, null);
}

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    //获取在IoC容器初始化过程中设置的资源加载器
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException(
                "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    }

    if (resourceLoader instanceof ResourcePatternResolver) {
        // Resource pattern matching available.
        try {
            //将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
            //加载多个指定位置的Bean定义资源文件
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
            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 {
        // Can only load single resources by absolute URL.
        //将指定位置的Bean定义资源文件解析为Spring IOC容器封装的资源
        //加载单个指定位置的Bean定义资源文件
        Resource resource = resourceLoader.getResource(location);
        //委派调用其子类XmlBeanDefinitionReader的方法,实现加载功能
        int loadCount = loadBeanDefinitions(resource);
        if (actualResources != null) {
            actualResources.add(resource);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
        }
        return loadCount;
    }
}

//重载方法,调用loadBeanDefinitions(String);
@Override
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;
}

loadBeanDefinitions(Resource...resources)样也是调用XmlBeanDefinitionReader的loadBeanDefinitions方法。

对AbstractBeanDefinitionReader的loadBeanDefinitions方法源码分析可以看出该方法:
首先,调用资源加载器的获取资源方法resourceLoader.getResource(location),获取到要加载的资源;
其次,真正执行加载功能是其子类XmlBeanDefinitionReader的loadBeanDefinitions方法。

(6)资源加载器获取要读入的资源

AbstractBeanDefinitionReader通过调用其父类DefaultResourceLoader的getResource方法获取要加载的资源,源码如下:

//获取Resource的具体实现方法
@Override
public Resource getResource(String location) {
    Assert.notNull(location, "Location must not be null");

    for (ProtocolResolver protocolResolver : this.protocolResolvers) {
        Resource resource = protocolResolver.resolve(location, this);
        if (resource != null) {
            return resource;
        }
    }
    //如果是类路径的方式,需要使用ClassPathResource来得到bean文件的资源对象
    if (location.startsWith("/")) {
        return getResourceByPath(location);
    }
    else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
        return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
    }
    else {
        try {
            // Try to parse the location as a URL...
            // 如果是URL方式,使用UrlResource作为bean文件的资源对象
            URL url = new URL(location);
            return (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
        }
        catch (MalformedURLException ex) {
            // No URL -> resolve as resource path.
            //如果既不是classpath标识,又不是URL标识的Resource定位,则调用容器本身的getResourceByPath方法获取Resource
            return getResourceByPath(location);
        }
    }
}

FileSystemXmlApplicationContext容器提供了getResourceByPath方法的实现,就是为了处理既不是classpath 标识,又不是URL标识的Resource定位这种情况。

@Override
protected Resource getResourceByPath(String path) {
    if (path.startsWith("/")) {
        path = path.substring(1);
    }
    //这里使用文件系统资源对象来定义bean文件
    return new FileSystemResource(path);
}

这样代码就回到了FileSystemXmlApplicationContext中来,他提供了FileSystemResource来完成从文件系统得到配置文件的资源定义。
这样就可以从文件系统路径上对IOC配置文件进行加载,当然也可以按照这个逻辑从任何地方加载,在Spring中可以看到它提供的各种资源抽象,
比如ClassPathResource、URLResource、FileSystemResource等来供我们使用。上面是定位Resource的一个过程,这只是加载过程的一部分.

(7)XmlBeanDefinitionReader加载Bean定义资源

回到XmlBeanDefinitionReader的loadBeanDefinitions(Resource …)方法看到代表bean文件的资源定义以后的载入过程。

//XmlBeanDefinitionReader加载资源的入口方法
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    //将读入的XML资源进行特殊编码处理
    return loadBeanDefinitions(new EncodedResource(resource));
}

//这里是载入XML形式Bean定义资源文件方法
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的IO流
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            //从InputStream中得到XML的解析源
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            //这里是具体的读取过程
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        finally {
            //关闭从Resource中得到的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();
        }
    }
}

//从特定XML文件中实际载入Bean定义资源的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    try {
        //将XML文件转换为DOM对象,解析过程由documentLoader实现
        Document doc = doLoadDocument(inputSource, resource);
        //这里是启动对Bean定义解析的详细过程,该解析过程会用到Spring的Bean配置规则
        return registerBeanDefinitions(doc, resource);
    }
    ...
}

通过源码分析,载入Bean定义资源文件的最后一步是将Bean定义资源转换为Document对象,该过程由documentLoader实现。

(8)DocumentLoader将Bean定义资源转换为Document对象

源码如下:

//使用标准的JAXP将载入的Bean定义资源转换成document对象
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
        ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

    //创建文件解析器工厂
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isDebugEnabled()) {
        logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    //创建文档解析器
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    //解析Spring的Bean定义资源
    return builder.parse(inputSource);
}

protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
        throws ParserConfigurationException {

    //创建文档解析工厂
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(namespaceAware);

    //设置解析XML的校验
    if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
        factory.setValidating(true);
        if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
            // Enforce namespace aware for XSD...
            factory.setNamespaceAware(true);
            try {
                factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
            }
            catch (IllegalArgumentException ex) {
                ParserConfigurationException pcex = new ParserConfigurationException(
                        "Unable to validate using XSD: Your JAXP provider [" + factory +
                        "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
                        "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
                pcex.initCause(ex);
                throw pcex;
            }
        }
    }
    return factory;
}

该解析过程调用JavaEE标准的JAXP标准进行处理。至此Spring IOC容器根据定位的Bean定义资源文件,将其加载读入并转换成为Document对象过程完成。
接下来继续分析Spring IOC容器将载入的Bean定义资源文件转换为Document对象之后,是如何将其解析为Spring IOC管理的Bean对象并将其注册到容器中的。

IOC容器初始化内容较多,分了几篇来写,此为第二篇,欢迎关注其余内容,感谢!

原文地址:https://www.cnblogs.com/yaofengdoit/p/12094055.html

时间: 2024-11-10 04:39:26

spring5源码分析系列(四)——IOC容器的初始化(二)的相关文章

spring5源码分析系列(五)——IOC容器的初始化(三)

前言:上一篇讲到了DocumentLoader将Bean定义资源转换为Document对象,此篇我们继续后面的内容. (9)XmlBeanDefinitionReader解析载入的Bean定义资源文件 XmlBeanDefinitionReader类中的doLoadBeanDefinitions方法是从特定XML文件中实际载入Bean定义资源的方法,该方法在载入Bean定义资源之后将其转换为Document对象, 接下来调用registerBeanDefinitions启动Spring IOC容

spring5源码分析系列(六)——IOC容器的初始化(四)

前言:上一篇讲到了解析子元素,此篇我们继续后面的内容. (15)解析过后的BeanDefinition在IOC容器中的注册 接下来分析DefaultBeanDefinitionDocumentReader对Bean定义转换的Document对象解析的流程中,在其parseDefaultElement方法中完成对Document对象的解析后得到封装BeanDefinition的BeanDefinitionHold对象, 然后调用BeanDefinitionReaderUtils的registerB

spring5源码分析系列(二)——spring核心容器体系结构

首先我们来认识下IOC和DI: IOC(Inversion of Control)控制反转:控制反转,就是把原先代码里面需要实现的对象创建.依赖的代码,反转给容器来帮忙实现.所以需要创建一个容器,并且需要一种描述来让容器知道需要创建的对象与对象的关系.这个描述最具体表现就是可配置的文件. DI(Dependency Injection)依赖注入:指对象是被动接受依赖类而不是自己主动去找,即对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它. 思考以下问题: 对象和

【spring源码分析】spring ioc容器之前生今世--DefaultListableBeanFactory源码解读

spring Ioc容器的实现,从根源上是beanfactory,但真正可以作为一个可以独立使用的ioc容器还是DefaultListableBeanFactory,因此可以这么说, DefaultListableBeanFactory 是整个spring ioc的始祖,研究透它的前生今世对我们理解spring ioc的概念有着重要的作用. 1. DefaultListableBeanFactory的作用: 默认实现了ListableBeanFactory和BeanDefinitionRegis

SpringMVC源码分析系列

说到java的mvc框架,struts2和springmvc想必大家都知道,struts2的设计基本上完全脱离了Servlet容器,而springmvc是依托着Servlet容器元素来设计的,同时springmvc基于Spring框架,Spring框架想必搞java的同学都很熟悉. 一进Spring的官网就发现了这样一排醒目的文字, spring可以让我们构造简单的.便携的.又快又易于扩展的基于jvm的系统和应用程序. 没错,基于Spring的MVC框架SpringMVC同样也可以构造具有这些特

jQuery-1.9.1源码分析系列完毕目录整理

jQuery 1.9.1源码分析已经完毕.目录如下 jQuery-1.9.1源码分析系列(一)整体架构 jQuery-1.9.1源码分析系列(一)整体架构续 jQuery-1.9.1源码分析系列(二)jQuery选择器 jQuery-1.9.1源码分析系列(二)jQuery选择器续1 jQuery-1.9.1源码分析系列(二)jQuery选择器续2——筛选 jQuery-1.9.1源码分析系列(三) Sizzle选择器引擎——词法解析 jQuery-1.9.1源码分析系列(三) Sizzle选择

Spark SQL 源码分析系列文章

从决定写Spark SQL源码分析的文章,到现在一个月的时间里,陆陆续续差不多快完成了,这里也做一个整合和索引,方便大家阅读,这里给出阅读顺序 :) 第一篇 Spark SQL源码分析之核心流程 第二篇 Spark SQL Catalyst源码分析之SqlParser 第三篇 Spark SQL Catalyst源码分析之Analyzer 第四篇 Spark SQL Catalyst源码分析之TreeNode Library 第五篇 Spark SQL Catalyst源码分析之Optimize

Spark 源码分析系列

如下,是 spark 源码分析系列的一些文章汇总,持续更新中...... Spark RPC spark 源码分析之五--Spark RPC剖析之创建NettyRpcEnv spark 源码分析之六--Spark RPC剖析之Dispatcher和Inbox.Outbox剖析 spark 源码分析之七--Spark RPC剖析之RpcEndPoint和RpcEndPointRef剖析 spark 源码分析之八--Spark RPC剖析之TransportContext和TransportClie

[Android]Volley源码分析(四)

上篇中有提到NetworkDispatcher是通过mNetwork(Network类型)来进行网络访问的,现在来看一下关于Network是如何进行网络访问的. Network部分的类图: Network有一个实现类BasicNetwork,它有一个mHttpStack的属性,实际的网络请求是由这个mHttpStack来进行的,看BasicNetwork的performRequest()方法, 1 @Override 2 public NetworkResponse performRequest