spring5 source code analysis series (four) - Initialization IOC container (b)

Introduction: Previous talked about Xml Bean reader (XmlBeanDefinitionReader) method call reader.loadBeanDefinitions its parent AbstractBeanDefinitionReader Bean read the definition of resources, we are moving on this post.

(5) AbstractBeanDefinitionReader method of loadBeanDefinitions

Source method as follows:

//重载方法,调用下面的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) is also a kind of calling loadBeanDefinitions method XmlBeanDefinitionReader.

LoadBeanDefinitions method of AbstractBeanDefinitionReader source code analysis can be seen this method:
First, call the resource loader method of access to resources resourceLoader.getResource (location), to obtain the resources to be loaded;
secondly, the real execution of the load function is a subclass of loadBeanDefinitions XmlBeanDefinitionReader method.

(6) The resource loader get to read resource

AbstractBeanDefinitionReader access to resources to be loaded by calling the parent class DefaultResourceLoader getResource method, the source code is as follows:

//获取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 container provides an implementation of the method, the processing is to identify neither classpath, are not identified by the URL Resource positioning case.

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

Such code is returned to the FileSystemXmlApplicationContext in the past, he provides a complete resource definition FileSystemResource to get the configuration file from the file system.
This can be carried out from the file system path to the configuration file is loaded IOC, of course, can be loaded from any place according to this logic, you can see all kinds of resources it provides abstraction in Spring,
such as ClassPathResource, URLResource, FileSystemResource like for us use. The above process is to locate a Resource, which is just part of the process of loading.

(7) XmlBeanDefinitionReader load the definition of resource Bean

Back XmlBeanDefinitionReader of loadBeanDefinitions (Resource ...) method to see the future on behalf of bean resource definition file loading process.

//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);
    }
    ...
}

By analyzing source code, loaded Bean defined resource file last step is to define the resource Bean converted to a Document object, the process is implemented by documentLoader.

(8) DocumentLoader Bean will be converted to defined resource Document object

Source as follows:

//使用标准的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;
}

The analytical procedure call JavaEE standard JAXP standard for processing. At this point Bean Spring IOC container according to the definition of the resource file location, load it reads and converted into a Document object process is complete.
Then we continue to analyze after the Spring IOC container will load the Bean defined resource file into a Document object, how it is resolved to Spring IOC Bean object management and registered with the container.

IOC container initialization more content, a few minutes to write, this is the second piece welcome attention to the rest, thanks!

Guess you like

Origin www.cnblogs.com/yaofengdoit/p/12094055.html