Spring 源码学习(二)—— xml 配置解析

        从之前对 Spring 基本上下文进行分析(Spring 源码学习(一)—— spring 上下文概览-优快云博客)我们可以发现对 XML 配置读取与解析,无论是 AbstractXmlApplicationContext 抽象类还是 GenericXmlApplicationContext 类都是通过 XmlBeanDefinitionReader 类进行的;同时从对下面 XmlBeanDefinitionReader 类的继承图的分析可以看到 XmlBeanDefinitionReader 类直接继承自 AbstractBeanDefinitionReader 抽象类,进而实现了 BeanDefinitionReader 与 EnvironmentCapable 接口;由于在上下文分析过程中已经对上述两个接口做过简单的分析了,因此本文就从 AbstractBeanDefinitionReader 抽象类出发来对基础 XML 配置文件的解析过程进行分析;

一 AbstractBeanDefinitionReader 抽象类

1 属性与相关方法

        AbstractBeanDefinitionReader 类拥有五个,即用于保存 BeanDefinitionReader 与 EnvironmentCapable 接口中定义的 get 方法所需要获取对应属性值,即 getRegistry 方法对应 registry 属性,getResourceLoader 方法对应 resourceLoader 属性、getBeanClassLoader 方法对应 beanClassLoader 属性、EnvironmentCapable 接口中的 getEnvironment 方法对应 environment 属性以及 getBeanNameGenerator 方法对应 beanNameGenerator 方法;其中 registry 属性使用 final 关键字进行修饰,其因此其必须在构造方法赋值,因此没有对应的 set 方法,其余属性都拥有对应的 set 方法;其中值得注意的是 resourceLoader 与 beanClassLoader 都是使用 Nullable 注解进行修饰,因此在其 set 方法中并未做任何非空验证,而 environment 属性的 set 方法采取在入参为空时直接抛出异常,beanNameGenerator 属性的 set 方法则是在入参为空时直接将其赋值为 DefaultBeanNameGenerator 类中 INSTANCE 常量值(DefaultBeanNameGenerator 对象)且对象在初始化时也会将其初始化为 DefaultBeanNameGenerator 对象;

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {

	private final BeanDefinitionRegistry registry;

	@Nullable
	private ResourceLoader resourceLoader;

	@Nullable
	private ClassLoader beanClassLoader;

	private Environment environment;

	private BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;

    @Override
	public final BeanDefinitionRegistry getRegistry() {
		return this.registry;
	}

	public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

	@Override
	@Nullable
	public ResourceLoader getResourceLoader() {
		return this.resourceLoader;
	}

	public void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {
		this.beanClassLoader = beanClassLoader;
	}

	@Override
	@Nullable
	public ClassLoader getBeanClassLoader() {
		return this.beanClassLoader;
	}

	public void setEnvironment(Environment environment) {
		Assert.notNull(environment, "Environment must not be null");
		this.environment = environment;
	}

	@Override
	public Environment getEnvironment() {
		return this.environment;
	}

	public void setBeanNameGenerator(@Nullable BeanNameGenerator beanNameGenerator) {
		this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : DefaultBeanNameGenerator.INSTANCE);
	}

	@Override
	public BeanNameGenerator getBeanNameGenerator() {
		return this.beanNameGenerator;
	}
}

2 构造方法

        AbstractBeanDefinitionReader 类只有一个带 BeanDefinitionRegistry 类型的 registry 参数的构造方法,对象创建过程中首先对其进行判空并在非空时将其值保存到 registry 属性中;随后在 registry 参数实现了 ResourceLoader 接口时同时会将其值保存到 resourceLoader 属性中,否则则将 resourceLoader 属性值初始化为 PathMatchingResourcePatternResolver 对象;最后在参数实现了 EnvironmentCapable 接口时则直接将 registry 参数的 getEnvironment 方法的执行结果值保存到 environment 属性之中,否则将 environment 属性值初始化为 StandardEnvironment 对象;

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {

	protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;

		if (this.registry instanceof ResourceLoader) {
			this.resourceLoader = (ResourceLoader) this.registry;
		}
		else {
			this.resourceLoader = new PathMatchingResourcePatternResolver();
		}

		if (this.registry instanceof EnvironmentCapable) {
			this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
		}
		else {
			this.environment = new StandardEnvironment();
		}
	}
}

3 loadBeanDefinitions 方法

3.1 Resource 类型参数的 loadBeanDefinitions 方法;

        AbstractBeanDefinitionReader 方法只实现了参数为 Resource 对象数组的 loadBeanDefinitions 方法,在方法执行过程中对 Resource 对象数组进行遍历,并调用单 Resource 对象参数的 loadBeanDefinitions 方法加载资源对象;

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
	@Override
	public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int count = 0;
		for (Resource resource : resources) {
			count += loadBeanDefinitions(resource);
		}
		return count;
	}
}

3.2 字符串路径参数的 loadBeanDefinitions 方法;

        参数为字符串数组的 loadBeanDefinitions 方法执行过程中对路径字符串参数进行遍历,并调用单字符串参数的 loadBeanDefinitions 方法加载资源对象;单字符串 loadBeanDefinitions 方法则是直接调用拥有 location 与 actualResources 参数的 loadBeanDefinitions 方法,并使用入参值作为 location 实参,actualResources 参数则直接传空;

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
	@Override
	public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
		Assert.notNull(locations, "Location array must not be null");
		int count = 0;
		for (String location : locations) {
			count += loadBeanDefinitions(location);
		}
		return count;
	}

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

        拥有 location 与 actualResources 参数的 loadBeanDefinitions 方法首先获取 getResourceLoader 方法结果值并将其保存到 resourceLoader 局部变量之中并对其进行非空验证;随后在该资源加载器实现了 ResourcePatternResolver 接口时调用其的 getResources 方法获取该路径下的所有资源对象同时使用获取的资源对象数组执行参数为 Resource 对象数组的 loadBeanDefinitions 方法加载获取到的所有资源,随后在 actualResources 入参不为空时,将获取到的资源数组全添加到 actualResources 入参之中并返回执行 loadBeanDefinitions 方法的执行结果值;否则在资源加载器未实现 ResourcePatternResolver 接口时,则直接调用其的 getResource 方法获取对应的资源对象并调用执行参数为 Resource 对象的 loadBeanDefinitions 方法加载获取到的资源,和之前一样在 actualResources 入参不为空时,将获取到的资源添加到 actualResources 入参之中并返回执行 loadBeanDefinitions 方法的执行结果值;

public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
	public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				int count = loadBeanDefinitions(resources);
				if (actualResources != null) {
					Collections.addAll(actualResources, resources);
				}
				if (logger.isTraceEnabled()) {
					logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
				}
				return count;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			Resource resource = resourceLoader.getResource(location);
			int count = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isTraceEnabled()) {
				logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
			}
			return count;
		}
	}
}

二 XmlBeanDefinitionReader 类

1 属性与相关方法

1.1 常量

        XmlBeanDefinitionReader 类拥有四个常量用于保存当前加载器所能使用的验证模式:其中 VALIDATION_NONE 常量表示关闭验证器,其实际值为 0;VALIDATION_AUTO 常量表示自动进行验证,其实际值为 1;VALIDATION_DTD 常量表示是否对 xml 使用 DTD 验证,其实际值为 2;VALIDATION_XSD 常量表示对 xml 使用 XSD 验证,其实际值为 3;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

	public static final int VALIDATION_NONE = XmlValidationModeDetector.VALIDATION_NONE;

	public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO;

	public static final int VALIDATION_DTD = XmlValidationModeDetector.VALIDATION_DTD;

	public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD;
}

1.2 对象属性

        XmlBeanDefinitionReader 类除了 namespaceHandlerResolver 与 entityResolver 属性之外其余属性都拥有默认值,其中 constant 属性用于缓存 XmlBeanDefinitionReader 类中的所有常量值;validationMode 属性则是保存当前对象将要使用的对象验证模式,初始化为 VALIDATION_AUTO 常量值;namespaceAware 属性用于标识 XML 是否需要支持命名空间,默认为 false 即不支持;documentReaderClass 用于设置 XML 配置文件的读取器类,默认使用 DefaultBeanDefinitionDocumentReader 类读取 XML 文件;problemReporter 属性则是用于定义异常报告器,默认使用 FailFastProblemReporter 对象;eventListener 属性则是保存对象读取事件监听器,用于对给定默认值成功注册事件、给定 Component 组件对象成功注册事件、给定别名对象成功注册事件及给定 import 注入对象成功注入事件的监听,默认初始化为 EmptyReaderEventListener,即任意事件都不会触发任何逻辑;sourceExtractor 属性用于提取 xml 配置元素的源并用于触发 Import 与 Alias 事件中的第三个参数,其默认值为 NullSourceExtractor 对象即直接返回 null;documentLoader 属性则是用于获取 Document 对象的属性,默认使用 DefaultDocumentLoader 对象;errorHandler 属性用于处理错误,默认使用 SimpleSaxErrorHandler 对象;validationModeDetector 使用 final 关键字修饰,其取值为 XmlValidationModeDetector 对象,用于检测XML流是使用基于 DTD 还是 XSD 的验证;最后一个属性为 resourcesCurrentlyBeingLoaded,其用于保存当前线程已加载的资源对象;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

	private static final Constants constants = new Constants(XmlBeanDefinitionReader.class);

	private int validationMode = VALIDATION_AUTO;

	private boolean namespaceAware = false;

	private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
			DefaultBeanDefinitionDocumentReader.class;

	private ProblemReporter problemReporter = new FailFastProblemReporter();

	private ReaderEventListener eventListener = new EmptyReaderEventListener();

	private SourceExtractor sourceExtractor = new NullSourceExtractor();

	@Nullable
	private NamespaceHandlerResolver namespaceHandlerResolver;

	private DocumentLoader documentLoader = new DefaultDocumentLoader();

	@Nullable
	private EntityResolver entityResolver;

	private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);

	private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector();
}

1.3 属性相关方法

        validationMode 属性的关联的 set 方法有三个,第一个只有一个用于标识是否开启文件验证的 boolean 类型参数 validating 的 setValidating,validating 为 true 时将 validationMode 属性值设置为 VALIDATION_AUTO 否则设置为 VALIDATION_NONE 并将 validating 参数取反保存到 namespaceAware 属性中;第二个方法则是 setValidationModeName 方法,其也只有一个参数,类型为字符串,其将字符串对应 XmlBeanDefinitionReader 中静态变量值保存到 validationMode 属性之中,若不存在对应的常量时会抛出异常;最后一个 set 方法为 validationMode 方法,其直接将传入的 int 类型参数值保存到 validationMode 属性中而不做任何其他处理;

        validationMode 属性还有一个直接获取其保存值的 get 方法 getValidationMode;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

	public void setValidating(boolean validating) {
		this.validationMode = (validating ? VALIDATION_AUTO : VALIDATION_NONE);
		this.namespaceAware = !validating;
	}

	public void setValidationModeName(String validationModeName) {
		setValidationMode(constants.asNumber(validationModeName).intValue());
	}

	public void setValidationMode(int validationMode) {
		this.validationMode = validationMode;
	}

	public int getValidationMode() {
		return this.validationMode;
	}
}

         namespaceAware 属性关联的方法有三个,一个就是之前看到的 setValidating 方法,其余两个方法直接设置值与获取值的 set 与 get 方法,没做其他任何特殊处理;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

	public void setNamespaceAware(boolean namespaceAware) {
		this.namespaceAware = namespaceAware;
	}

	public boolean isNamespaceAware() {
		return this.namespaceAware;
	}
}

         problemReporter、eventListener、sourceExtractor、namespaceHandlerResolver、documentLoader、errorHandler 以及 documentReaderClass 属性只设置了 set 方法而没有 get 方法,这意味着其会在内部使用而不向外暴露;其次就是除 namespaceHandlerResolver 与 documentReaderClass 属性的 set 方法之外,其他 set 方法在入参值为空时会为将属性更新为默认值,其具体的默认值在属性解析过程中已分析过;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

	public void setProblemReporter(@Nullable ProblemReporter problemReporter) {
		this.problemReporter = (problemReporter != null ? problemReporter : new FailFastProblemReporter());
	}

	public void setEventListener(@Nullable ReaderEventListener eventListener) {
		this.eventListener = (eventListener != null ? eventListener : new EmptyReaderEventListener());
	}

	public void setSourceExtractor(@Nullable SourceExtractor sourceExtractor) {
		this.sourceExtractor = (sourceExtractor != null ? sourceExtractor : new NullSourceExtractor());
	}

	public void setNamespaceHandlerResolver(@Nullable NamespaceHandlerResolver namespaceHandlerResolver) {
		this.namespaceHandlerResolver = namespaceHandlerResolver;
	}

	public void setDocumentLoader(@Nullable DocumentLoader documentLoader) {
		this.documentLoader = (documentLoader != null ? documentLoader : new DefaultDocumentLoader());
	}

	public void setErrorHandler(ErrorHandler errorHandler) {
		this.errorHandler = errorHandler;
	}

	public void setDocumentReaderClass(Class<? extends BeanDefinitionDocumentReader> documentReaderClass) {
		this.documentReaderClass = documentReaderClass;
	}
}

          entityResolver 属性提供了 get 与 set 方法,其中 set 方法直接将入参值保存到 entityResolver 属性之中,没什么好分析的;get 方法则是在 entityResolver 属性不为空时返回其保存值,否则在 getResourceLoader 方法结果不为空时,使用获取到的资源加载器创建 ResourceEntityResolver 对象并将其值保存到 entityResolver 属性之中然后返回,否则在 getResourceLoader 方法结果为空时则使用 getBeanClassLoader 获取到的类加载器创建 DelegatingEntityResolver 对象并将其值保存到 entityResolver 属性之中然后返回。

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

	protected EntityResolver getEntityResolver() {
		if (this.entityResolver == null) {
			ResourceLoader resourceLoader = getResourceLoader();
			if (resourceLoader != null) {
				this.entityResolver = new ResourceEntityResolver(resourceLoader);
			}
			else {
				this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
			}
		}
		return this.entityResolver;
	}

	public void setEntityResolver(@Nullable EntityResolver entityResolver) {
		this.entityResolver = entityResolver;
	}
}

2 loadBeanDefinitions 方法

2.1 父类的 loadBeanDefinitions 方法实现

        loadBeanDefinitions 方法实现中使用 EncodedResource 对 resource 参数进行封装并使用封装值调用只有一个 EncodedResource 参数的 loadBeanDefinitions 方法;

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

         只有一个 EncodedResource 参数的 loadBeanDefinitions 方法首先对 encodedResource 参数进行非空验证同时将 encodedResource 参数添加到 resourcesCurrentlyBeingLoaded 属性中当前线程保存的已经加载资源 set 集合中;随后获取 encodedResource 参数中封装的资源流同时使用该流创建 InputSource 对象并保存到 inputSource 局部变量之中,在 encodedResource 的 encoding 属性不为空时,将其保存的编码格式保存到 inputSource 变量中,随后使用 InputSource 变量与 encodedResource 参数中封装的 Resource 资源对象执行 doLoadBeanDefinitions 方法并返回其结果;最后若当前线程中的 encodedResource 参数已经成功添加则将会从 resourcesCurrentlyBeingLoaded 属性中的当前线程中移除该参数。

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isTraceEnabled()) {
			logger.trace("Loading XML bean definitions from " + encodedResource);
		}

		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}

		try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
			InputSource inputSource = new InputSource(inputStream);
			if (encodedResource.getEncoding() != null) {
				inputSource.setEncoding(encodedResource.getEncoding());
			}
			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}
}

2.2 拥有 inputSource 参数的 loadBeanDefinitions 方法

        XmlBeanDefinitionReader 类中有两个拥有 inputSource 参数的 loadBeanDefinitions 方法,第一个方法只有一个 inputSource 参数,其内部直接调用第二个 loadBeanDefinitions 方法;第二个方法用于两个参数,其直接使用 inputSource 与使用 resourceDescription 创建的 DescriptiveResource 对象执行 doLoadBeanDefinitions 方法并返回其执行结果。

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	public int loadBeanDefinitions(InputSource inputSource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(inputSource, "resource loaded through SAX InputSource");
	}

	public int loadBeanDefinitions(InputSource inputSource, @Nullable String resourceDescription)
			throws BeanDefinitionStoreException {

		return doLoadBeanDefinitions(inputSource, new DescriptiveResource(resourceDescription));
	}
}

2.3 doLoadBeanDefinitions 方法

          doLoadBeanDefinitions 方法首先调用 doLoadDocument 方法将 inputSource 资源加载为 XML Document 对象并利用该结果与 resource 参数值执行 registerBeanDefinitions 方法并返回其结果值;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
			Document doc = doLoadDocument(inputSource, resource);
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		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);
		}
	}
}
2.3.1 doLoadDocument 方法

        doLoadDocument 方法中直接调用 documentLoader 属性中的 loadDocument 方法,该方法拥有 inputSource、entityResolver、errorHandler、validationMode 及 namespaceAware 五个参数,其中 inputSource 参数直接使用 doLoadDocument 方法的 inputSource 参数值,errorHandler 参数则是使用 errorHandler 属性值,entityResolver、validationMode 与 namespaceAware 三个参数分别使用 getEntityResolver、getValidationModeForResource 及 isNamespaceAware 方法的执行结果值;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
		return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
				getValidationModeForResource(resource), isNamespaceAware());
	}
}

        getValidationModeForResource 方法首先执行 getValidationMode 获取 validationMode 属性值并在其值不为 VALIDATION_AUTO 时直接返回该值,否则执行 detectValidationMode 方法并将在其执行结果值不为 VALIDATION_AUTO 时直接返回其值,否则直接返回 VALIDATION_XSD;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	protected int getValidationModeForResource(Resource resource) {
		int validationModeToUse = getValidationMode();
		if (validationModeToUse != VALIDATION_AUTO) {
			return validationModeToUse;
		}
		int detectedMode = detectValidationMode(resource);
		if (detectedMode != VALIDATION_AUTO) {
			return detectedMode;
		}
		return VALIDATION_XSD;
	}
}

         detectValidationMode 方法在 isOpen 执行结果为 true,即当前资源已经被打开了时直接抛出异常,然后获取资源的输入流并利用 validationModeDetector 属性的 detectValidationMode 方法获取资源的验证模式并返回。

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	protected int detectValidationMode(Resource resource) {
		if (resource.isOpen()) {
			throw new BeanDefinitionStoreException(
					"Passed-in Resource [" + resource + "] contains an open stream: " +
					"cannot determine validation mode automatically. Either pass in a Resource " +
					"that is able to create fresh streams, or explicitly specify the validationMode " +
					"on your XmlBeanDefinitionReader instance.");
		}

		InputStream inputStream;
		try {
			inputStream = resource.getInputStream();
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
					"Did you attempt to load directly from a SAX InputSource without specifying the " +
					"validationMode on your XmlBeanDefinitionReader instance?", ex);
		}

		try {
			return this.validationModeDetector.detectValidationMode(inputStream);
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
					resource + "]: an error occurred whilst reading from the InputStream.", ex);
		}
	}
}
2.3.2 registerBeanDefinitions 方法

        registerBeanDefinitions 方法首先调用 createBeanDefinitionDocumentReader 创建 documentReaderClass 属性对应的 BeanDefinitionDocumentReader 对象并保存到 documentReader 局部变量中;随后利用 resource 参数执行 createReaderContext 方法创建 XmlReaderContext 对象并利用创建的对象与 doc 参数执行 documentReader 变量的 registerBeanDefinitions 方法将对象注册表注册到 registry 属性中;最后返回本资源加载之前已注册的对象注册表数量与加载之后的对象注册表数量之间的差值;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

	protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
		return BeanUtils.instantiateClass(this.documentReaderClass);
	}
}

         createReaderContext 方法使用 resource 参数、problemReporter、eventListener、eventListener属性、当前读取器对象以及 getNamespaceHandlerResolver 方法执行结果创建 XmlReaderContext 对象并返回;

        getNamespaceHandlerResolver 方法在 namespaceHandlerResolver 属性不为空时执行 createDefaultNamespaceHandlerResolver 方法创建 DefaultNamespaceHandlerResolver 对象并将其保存到 namespaceHandlerResolver 属性赋值,最后返回 namespaceHandlerResolver 属性值。

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
	public XmlReaderContext createReaderContext(Resource resource) {
		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
				this.sourceExtractor, this, getNamespaceHandlerResolver());
	}

	public NamespaceHandlerResolver getNamespaceHandlerResolver() {
		if (this.namespaceHandlerResolver == null) {
			this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
		}
		return this.namespaceHandlerResolver;
	}

	protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
		ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
		return new DefaultNamespaceHandlerResolver(cl);
	}
}

三 Resource 资源组

        XmlBeanDefinitionReader 类中的核心方法为 loadBeanDefinitions,其入参为保存 xml 配置文件资源的 Rource 对象,因此本节就针对所有相关的 Rource 接口与类对 xml 配置源的进行分析; 

1 接口

1.1 InputStreamSource

        InputStreamSource 接口为 Resource 组的顶级接口,其只有一个用于向外提供获取输入流 InputStream 对象的 getInputStream 方法;

public interface InputStreamSource {

	InputStream getInputStream() throws IOException;

}

1.2 Resource

        Resource 接口提供读取当前资源的基础方法;其中 exists、isReadable、isOpen 与 isFile 用于获取资源状态,分别获取当前资源是否还存在、是否可读、是否打开以及是否为文件属性;getURL 、getURI 与 getFile 三个方法用于获取资源相关实体,其中 getURL 方法用于获取与资源绑定的 URL 对象、getURI 方法用于获取与资源绑定的 URI 对象以及 getFile 方法用于获取与资源绑定的 File 对象;readableChannel 方法则是获取当前资源的只读通道 ReadableByteChannel 对象;contentLength 与 lastModified 方法分别获取当前资源的内容长度与最后一次修改时间戳;createRelative 方法为当前资源创建与其关联的相对路径;最后两个方法,getFilename 与 getDescription 分别用于获取文件名及其描述;

public interface Resource extends InputStreamSource {

	boolean exists();

	default boolean isReadable() {
		return exists();
	}

	default boolean isOpen() {
		return false;
	}

	default boolean isFile() {
		return false;
	}

	URL getURL() throws IOException;

	URI getURI() throws IOException;

	File getFile() throws IOException;

	default ReadableByteChannel readableChannel() throws IOException {
		return Channels.newChannel(getInputStream());
	}

	long contentLength() throws IOException;

	long lastModified() throws IOException;

	Resource createRelative(String relativePath) throws IOException;

	@Nullable
	String getFilename();

	String getDescription();

}

1.3 WritableResource

        WritableResource 接口扩展了 Resource 接口的功能,向外提供对当前资源的更改功能;该接口提供了三个方法,isWritable 方法用于获取当前资源是否可读,默认实现为直接返回 true;getOutputStream 方法用于获取当前资源相关的输出流;最后一个 writableChannel 方法与 Resource 接口中的 readableChannel 方法相对应用于获取可写通道;

public interface WritableResource extends Resource {

	default boolean isWritable() {
		return true;
	}

	OutputStream getOutputStream() throws IOException;

	default WritableByteChannel writableChannel() throws IOException {
		return Channels.newChannel(getOutputStream());
	}

}

2 AbstractResource 抽象类

2.1 状态判断方法

        exists 方法在当前资源是文件是直接判断文件是否存在,否则通过执行输入流对象关闭方法来判断当前资源是否存在;isReadable 方法则是直接执行 exists 方法并返回结果;isOpen 与 isFile 方法都直接返回 false; 

public abstract class AbstractResource implements Resource {

	@Override
	public boolean exists() {
		if (isFile()) {
			try {
				return getFile().exists();
			}
			catch (IOException ex) {
				Log logger = LogFactory.getLog(getClass());
				if (logger.isDebugEnabled()) {
					logger.debug("Could not retrieve File for existence check of " + getDescription(), ex);
				}
			}
		}
		try {
			getInputStream().close();
			return true;
		}
		catch (Throwable ex) {
			Log logger = LogFactory.getLog(getClass());
			if (logger.isDebugEnabled()) {
				logger.debug("Could not retrieve InputStream for existence check of " + getDescription(), ex);
			}
			return false;
		}
	}

	@Override
	public boolean isReadable() {
		return exists();
	}

	@Override
	public boolean isOpen() {
		return false;
	}

	@Override
	public boolean isFile() {
		return false;
	}
}

2.2 资源返回方法

        由于当前类未绑定任何实际资源,因此 getURL 与 getFile方法两个直接获取资源对象的方法不会返回任何对象而直接抛出异常;而 getURI 方法则首先通过 getURL 方法获取关联的 URL 对象,并调用 ResourceUtils 类中的 toURI 将其转换为 URI 对象并返回(由于该方法调用了 getURL 方法,在子类中未重写 getURL 方法时同样也会直接抛出异常); 

public abstract class AbstractResource implements Resource {
	@Override
	public URL getURL() throws IOException {
		throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
	}

	@Override
	public URI getURI() throws IOException {
		URL url = getURL();
		try {
			return ResourceUtils.toURI(url);
		}
		catch (URISyntaxException ex) {
			throw new NestedIOException("Invalid URI [" + url + "]", ex);
		}
	}

	@Override
	public File getFile() throws IOException {
		throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
	}
}

2.3 ReadableByteChannel 方法

        ReadableByteChannel 方法直接使用 getInputStream 方法获取当前资源的输入流创建可读通道 ReadableByteChannel 对象并返回; 

public abstract class AbstractResource implements Resource {
	@Override
	public ReadableByteChannel readableChannel() throws IOException {
		return Channels.newChannel(getInputStream());
	}
}

2.4 contentLength 方法

        contentLength 方法通过对输入流的读取来统计当前资源所包含的字节数; 

public abstract class AbstractResource implements Resource {
	@Override
	public long contentLength() throws IOException {
		InputStream is = getInputStream();
		try {
			long size = 0;
			byte[] buf = new byte[256];
			int read;
			while ((read = is.read(buf)) != -1) {
				size += read;
			}
			return size;
		}
		finally {
			try {
				is.close();
			}
			catch (IOException ex) {
				Log logger = LogFactory.getLog(getClass());
				if (logger.isDebugEnabled()) {
					logger.debug("Could not close content-length InputStream for " + getDescription(), ex);
				}
			}
		}
	}
}

2.5 lastModified 方法

        lastModified 方法首先调用 getFileForLastModifiedCheck 方法获取用于时间戳验证的文件对象并调用其 lastModified 方法获取上次修改时间戳值;随后在时间戳值为 0 且文件不存在时直接抛出异常并在通过验证后返回时间戳值。 

public abstract class AbstractResource implements Resource {
	@Override
	public long lastModified() throws IOException {
		File fileToCheck = getFileForLastModifiedCheck();
		long lastModified = fileToCheck.lastModified();
		if (lastModified == 0L && !fileToCheck.exists()) {
			throw new FileNotFoundException(getDescription() +
					" cannot be resolved in the file system for checking its last-modified timestamp");
		}
		return lastModified;
	}

	protected File getFileForLastModifiedCheck() throws IOException {
		return getFile();
	}
}

2.6 createRelative 与 getFilename 方法

        createRelative 方法未做任何实现,直接抛出异常,而最后一个 getFilename 方法直接返回 null。 

public abstract class AbstractResource implements Resource {
	@Override
	public Resource createRelative(String relativePath) throws IOException {
		throw new FileNotFoundException("Cannot create a relative resource f
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值