三、Spring MVC 源码学习(BeanDefinitionReader)

一、BeanDefinitionReader体系结构

Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成:

这里写图片描述

AbstractBeanDefinitionReader:实现了EnvironmentCapable,提供了获取/设置环境的方法。定义了一些通用方法,使用策略模式,将一些具体方法放到子类实现。 

二、接口设计

       1.BeanDefinitionReader

public interface BeanDefinitionReader {
	BeanDefinitionRegistry getRegistry();
	ResourceLoader getResourceLoader();
	ClassLoader getBeanClassLoader();
	BeanNameGenerator getBeanNameGenerator()
	int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
	int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
	int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
	int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}

三、AbstractBeanDefinitionReader核心

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) {
			// 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 {
			// Can only load single resources by absolute URL.
			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;
		}
	}

1.调用资源加载器的获取资源方法resourceLoader.getResource(location),获取到要加载的资源(FileSystemXmlApplicationContext继承自DefaultResourceLoader )。

2.其次,真正执行加载功能是其子类XmlBeanDefinitionReader 的 loadBeanDefinitions 方法。

四、XmlBeanDefinitionReader核心

1.加载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 {
    .......
    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);
    }
   ...........
}

//从特定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);
    }
    catch (BeanDefinitionStoreException ex) {
        throw ex;
    }
   .....
}

2.将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 对象并将其注册到容器中的。

3.注册定义在DOM document中的BeanDefinition

//按照Spring的Bean语义要求将Bean定义资源解析并转换为容器内部数据结构
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //得到BeanDefinitionDocumentReader来对xml格式的BeanDefinition解析
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    //获得容器中注册的Bean数量  
    int countBefore = getRegistry().getBeanDefinitionCount();
    //解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader只是个接口,
    //具体的解析实现过程有实现类DefaultBeanDefinitionDocumentReader完成
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    //统计解析的Bean数量
    return getRegistry().getBeanDefinitionCount() - countBefore;
}

   分析:1.getRegistry()获取到的注册器哪里来的?

                 XMLBeanDefinionReader 持有BeanFactoty 的引用,而beanFactory又实现了BeanDefinitionRegistry接口,因此这里获取到的是工厂

                  

五、总结

     XMLBeanDefinionReader.loadBeanDefinitions(String... locations) 执行流程

                 1.循环locations  对每一个location执行 以下方法(父类完成)

                 2.获取location对应的resource(父类完成)

                 3.将resource解析为dom文档

                 4.解析dom为BeanDefinition,并将其注册到工厂

​​​​​​​​

猜你喜欢

转载自blog.csdn.net/qq2413273056/article/details/82592462
今日推荐