`
游伯度
  • 浏览: 22836 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring IOC - 自定义解析Bean

阅读更多

本次分享还是要重复上一次分享的内容(Spring IOC - BeanDefinition注册),着重 Bean 的注册过程。上次我们说到 Bean 通过解析后被封装为 BeanDefinitionHolder 中,最终存放在 beanDefinitionMap 中。那么对应自定义 schema 的 Bean 解析 Spring 是怎么做到的,他又是怎样被注册到容器中的呢,这个两个问题,是我们这次要分析的。

 

在进行 Spring 源码分析前,如果你还不了解如何配置自己的 schema 来定义 Bean 你可以在网上自己查询一下,或者可以看看这个分享(自定义 Schema 解析 Spring Bean),可能对你有些帮助。

 

下面我们正式开始:

 

我们了解到最终解析自定义 schema 来解析 Bean 定义时,最终在 BeanDefinitionParserDelegate 的parseCustomElement方法中处理。源代码如下:

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
	// 获取需要解析的Bean定义命名空间
	String namespaceUri = getNamespaceURI(ele);
	// 获取对应的命名空间处理器
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	// 如果获取不到对应的命名空间处理器,说明出现了异常
	if (handler == null) {
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
	// 使用对应的命名空间处理器进行解析,得到 Bean 定义
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

 

通过上面的代码,我们关心是下面几点

  • readerContext.getNamespaceHandlerResolver 得到真正实例是什么?
  • NamespaceHandlerResolver.resolve 怎么处理命名空间?
  • NamespaceHandler.parse 如何扩展实现的?

 

下面我们就上面几个问题进行解答:

首先从 readerContext 入手,看看这个实例是什么时间,什么地方,怎么构建的?

 

在“BeanDefinition注册”分享中,我们我们讲到了当 Spring 找到了资源所在的位置,那么下一步就是解析资源、注册 Bean 定义。其中注册 Bean 定义的入口在org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions中。源码如下:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	// 生成 BeanDefinitionDocumentReader
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	documentReader.setEnvironment(this.getEnvironment());
	int countBefore = getRegistry().getBeanDefinitionCount();
	// 此处注册 bean定义,并且创建前,构建了 ReadContext,作为入参
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	return getRegistry().getBeanDefinitionCount() - countBefore;
}
protected XmlReaderContext createReaderContext(Resource resource) {
	// 此处我们发现有创建 NamespaceHandlerResolver 的触发点
	if (this.namespaceHandlerResolver == null) {
		this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
	}
	// 创建 ReaderContext。其中 resource (资源文件)、problemReporter (问题记录)
	// eventListener(事件监听器)、sourceExtractor(资源提取器)、this(容器引用)、
	// namespaceHandlerResolver(命名空间处理器的判定器) 被传入其中。
	return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
			this.sourceExtractor, this, this.namespaceHandlerResolver);
}

先把 ReaderContext 说明完成,说明 namespaceHanderResolver。

 

看看 DefaultBeanDefinitionDocumentReader 中的 registerBeanDefinition 方法,了解 ReadContext 的传递过程:

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	// 把在 XmlBeanDefinitionReader 中构建好的 readerContext 赋值给 DefaultBeanDefinitionDocumentReader
	this.readerContext = readerContext;

	logger.debug("Loading bean definitions");
	Element root = doc.getDocumentElement();
	
	// 执行真正的解析过程
	doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
	String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
	if (StringUtils.hasText(profileSpec)) {
		Assert.state(this.environment != null, "environment property must not be null");
		String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		if (!this.environment.acceptsProfiles(specifiedProfiles)) {
			return;
		}
	}

	// any nested <beans> elements will cause recursion in this method. In
	// order to propagate and preserve <beans> default-* attributes correctly,
	// keep track of the current (parent) delegate, which may be null. Create
	// the new (child) delegate with a reference to the parent for fallback purposes,
	// then ultimately reset this.delegate back to its original (parent) reference.
	// this behavior emulates a stack of delegates without actually necessitating one.
	BeanDefinitionParserDelegate parent = this.delegate;
	// 把 readerContext 作为入参构建 Bean 解析用的 delegate
	this.delegate = createHelper(readerContext, root, parent);

	preProcessXml(root);
	parseBeanDefinitions(root, this.delegate);
	postProcessXml(root);

	this.delegate = parent;
}

protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
	// 通过 readerContext 来构建 BeanDefinitionParserDelegate,至此 readerContext 终于到了解析会使用的地方
	BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext, environment);
	delegate.initDefaults(root, parentDelegate);
	return delegate;
}

 

ReaderContext 在解析 Bean 定义前创建,同时包含了解析需要的NamespaceHandlerResolver, 下面我们看看 NamespaceHandlerResolver 的创建过程:

 

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#createDefaultNamespaceHandlerResolver

protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
	// 创建默认的命名空间处理器的判决处理器
	return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
}

 

下面我们来解读 DefaultNamespaceHandlerResolver:

/**
 * 使用命名空间处理器判断的默认配置文件来构建命名空间处理器判定器。
 *  默认配置的存放路径是在类路径的“META-INF/spring.handlers” 中。
 * Create a new <code>DefaultNamespaceHandlerResolver</code> using the
 * default mapping file location.
 * @param classLoader the {@link ClassLoader} instance used to load mapping resources
 * (may be <code>null</code>, in which case the thread context ClassLoader will be used)
 * @see #DEFAULT_HANDLER_MAPPINGS_LOCATION
 */
public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
	this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}

/**
 * Create a new <code>DefaultNamespaceHandlerResolver</code> using the
 * supplied mapping file location.
 * @param classLoader the {@link ClassLoader} instance used to load mapping resources
 * may be <code>null</code>, in which case the thread context ClassLoader will be used)
 * @param handlerMappingsLocation the mapping file location
 */
public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
	Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
	this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
	this.handlerMappingsLocation = handlerMappingsLocation;
}

 

下面我们找到 spring-bean-3.1.2.RELEASE.jar (我使用的是spring 3.1.2,并且每个包都是单独引入的)包中的META-INF/spring.handlers,了解里面的配置内容:

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

这个文件定义了命名空间对应的命名空间处理器。例如http\://www.springframework.org/schema/p对应的命名空间处理器是SimplePropertyNamespaceHandler。这个命名空间负责对默认属性赋值的处理。

具体的看一下源码,了解一下对命名空间配置的解析过程:

/**
 * 查找提供的命名空间标识(URI)对应的命名空间处理器
 * Locate the {@link NamespaceHandler} for the supplied namespace URI
 * from the configured mappings.
 * @param namespaceUri the relevant namespace URI
 * @return the located {@link NamespaceHandler}, or <code>null</code> if none found
 */
public NamespaceHandler resolve(String namespaceUri) {
	// 配置文件的解析过程,配置文件解析成为命名空间URI为KEY,命名空间处理器类为 VALUE 的
	// Map 集合。后面会分析具体的加载过程。
	Map<String, Object> handlerMappings = getHandlerMappings();
	Object handlerOrClassName = handlerMappings.get(namespaceUri);
	// 如果无法通过命名空间定位符获取到值,直接返回
	if (handlerOrClassName == null) {
		return null;
	}
	// 如果对应的KEY获取的值是命名空间处理器实例,返回这个命名空间处理器
	// 当经过反射实例化后,对应的值已经变成了类的实例,不是类命名了
	else if (handlerOrClassName instanceof NamespaceHandler) {
		return (NamespaceHandler) handlerOrClassName;
	}
	// 实例化后第一次解析配置,懒加载
	else {
		String className = (String) handlerOrClassName;
		try {
			// 判断对应的命名空间处理器类是否加载到JVM中,同时判断配置的类是否是命名空间处理器类
			Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
			if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
				throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
						"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
			}
			// 根据命名空间处理器类实例化
			NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
			// 初始化命名空间处理器
			namespaceHandler.init();
			// 把存放URI对应的值转换为对应的处理器实例
			handlerMappings.put(namespaceUri, namespaceHandler);
			// 返回处理器
			return namespaceHandler;
		}
		catch (ClassNotFoundException ex) {
			throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
					namespaceUri + "] not found", ex);
		}
		catch (LinkageError err) {
			throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
					namespaceUri + "]: problem with handler class file or dependent class", err);
		}
	}
}

/**
 * 加载指定的命名空间匹配,懒加载
 * Load the specified NamespaceHandler mappings lazily.
 */
private Map<String, Object> getHandlerMappings() {
	if (this.handlerMappings == null) {
		synchronized (this) {
			if (this.handlerMappings == null) {
				try {
					// 读取所有的配置文件,默认配置文件的命名是 “META-INF/spring.handlers”
					// 也就是说所有类路径下面的 “META-INF/spring.handlers”都会被加载到配置文件中
					Properties mappings =
							PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
					if (logger.isDebugEnabled()) {
						logger.debug("Loaded NamespaceHandler mappings: " + mappings);
					}
					Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>();
					CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
					this.handlerMappings = handlerMappings;
				}
				catch (IOException ex) {
					throw new IllegalStateException(
							"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
				}
			}
		}
	}
	return this.handlerMappings;
}

 

自定义 schema 解析器

 

一般情况我们自定义解析器都会继承于org.springframework.beans.factory.xml.NamespaceHandlerSupport,NamespaceHandlerSupport实现NamespaceHandler。NamespaceHandlerSupport实现是维护了一个 Xml Element 名称到BeanDefinitionParser映射和一个 xml Element 名称到BeanDefinitionDecorator的映射。前者负责负责解析工作,后者负责判断是否需要装饰,进行装饰替换。

BeanDefinitionParser 一般都会继承基础的org.springframework.beans.factory.xml.AbstractBeanDefinitionParser。AbstractBeanDefinitionParser实现了BeanDefinitionParser。如果你可以判断是单例 Bean 定义,可以继承org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser。Spring 提供了丰富的解析器,供选择使用。

 

至此,我们熟悉了整个自定义命名处理器的调用过程。并且通过上面的分析,我们应该了解到,如果我们需要扩展自己的 schema 来做 bean 定义时,我们需要配置自己独有的命名空间处理器,这个处理器配置在“META-INF/spring.handlers”即可。Spring 会查找类路径下面所有“META-INF/spring.handlers”,从而把我们个性化的配置加载其中。

 

我们知道解析 xml 文件时,我们需要通过 dtd 或 xsd 来验证配置文件的正确性。同时这些文件也为我们书写时提供标准,像我们常用的 eclipse 或者 idea 都能在书写的过程中验证我们 Bean 定义文件书写的正确性。那么spring 做了些什么工作,来支持解析 xml 时通过我们xsd文件来验证的呢?

 

Spring 解析 bean 配置文件是使用的 JDK 自带的 DOM 解析方式。这种方式的使用方法可以参考 JDK API 文档。其中 DOM 解析提供客户端可以指定指定URI或本地的 xsd 文件。下面是需要使用的 JDK API

DocumentBuilder.setEntityResolver(EntityResolver) : 指定使用 EntityResolver 解析要解析的 XML 文档中存在的实体。将其设置为 null 将会导致底层实现使用其自身的默认实现和行为。

 

关于 EntityResolver :下面是JDK的文档说明

用于解析实体的基本接口。

此模块(包括源代码和文档)在公共域中,同时没有担保。有关更多信息,请参阅http://www.saxproject.org

如果 SAX 应用程序需要实现自定义处理外部实体,则必须实现此接口并使用 setEntityResolver 方法向 SAX 驱动器注册一个实例。

然后 XML 阅读器将允许应用程序在包含外部实体之前截取任何外部实体(包括外部 DTD 子集和外部参数实体,如果有)。

许多 SAX 应用程序不需要实现此接口,但对于从数据库或其他特定的输入源中构建 XML 文档的应用程序,或者对于使用 URI 类型(而不是 URL )的应用程序,这特别有用。

 

下面的解析器将使用系统标识符 "http://www.myhost.com/today" 为应用程序提供用于实体的特定字符流:

import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;

public class MyResolver implements EntityResolver {
    public InputSource resolveEntity (String publicId, String systemId) {
        if (systemId.equals("http://www.myhost.com/today")) {
            // return a special input source
            MyReader reader = new MyReader();
            return new InputSource(reader);
        } else {
            // use the default behaviour
            return null;
        }
    }
}

 应用程序还可以使用此接口将系统标识符重定向到本地 URI,或者在目录中查找替换(可能使用公共标识符)。

 

这个接口中方法 resolveEntity API 文档解释:

InputSource resolveEntity(String publicId,String systemId)

                          throws SAXException,IOException允许应用程序解析外部实体。

解析器将在打开任何外部实体(顶级文档实体除外)前调用此方法。此类实体包括在 DTD 内引用的外部 DTD 子集和外部参数实体(无论哪种情形,仅在在解析器都读取外部参数实体时)和在文档元素内引用的外部通用实体(如果解析器读取外部通用实体)。应用程序可以请求解析器本身定位实体、使用另外的 URI或者使用应用程序提供的数据(作为字符或字节输入流)。

应用程序编写者可以使用此方法将外部系统标识符重定向到安全的和/或本地的 URI,以便在目录中查找公共标识符或从数据库或其他输入源(其中包括对话框)中读取实体。XML 和 SAX 都不为使用公共或系统 ID 解析资源指定首选策略。但是,SAX 指定了如何解释通过此方法返回的任何 InputSource,并指定如果未返回,则系统 ID 将被重新引用为 URL。

如果系统标识符是 URL,则 SAX 解析器必须在将它报告给应用程序之前完整解析它。

参数:

publicId - 被引用的外部实体的公共标识符,如果未提供,则为 null。

systemId - 被引用的外部实体的系统标识符。

返回:

一个描述新输入源的 InputSource 对象,或者返回 null,以请求解析器打开到系统标识符的常规 URI 连接。

抛出:

SAXException - 任何 SAX 异常,可能包装另外的异常。

IOException - 特定于 Java 的 IO 异常,可能是由于为 InputSource 创建新的 InputStream 或 Reader 所导致的。

 

通过上面 API 解释,我们了解到 Spring 只需要配置自己的 EntityResolver 就可以完成扩展,事实上 Spring 也就是这样做的。

在org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions中引用了 DocumentLoader  来执行 Bean 定义文件的读入,这个过程中指定了 EntityResovler。

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

 

在前面资源装载的分享中提到过当需要加载xml资源的时候,提前设置好了实体处理器。

org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)中有

beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));的处理操作。

 

ResourceEntityResolver是继承DelegatingEntityResolver的,他们核心目的就是读取xml 实体定义。前者的实现是当后者无法获取 xml 实体定义的时候,通过指定的 ResourceLoader 在通过配置的URI再查询一遍(可能是本地,也可能是联网货主其他等,取决配置),进行加载。

 

下面我们来分析一下 Spring 相关源码:

 

org.springframework.beans.factory.xml.ResourceEntityResolver#resolveEntity

 

public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
	// 调用超类实现,读入本地实体定义
	InputSource source = super.resolveEntity(publicId, systemId);
	// 如果读取不到本地实体定义
	if (source == null && systemId != null) {
		String resourcePath = null;
		try {
			// 尝试文件方式查询实体定义文件
			// 例如配置为 : file:/F:/experiment/schema/abc.xsd,通过解析后得到地址为 /abc.xsd
			String decodedSystemId = URLDecoder.decode(systemId);
			String givenUrl = new URL(decodedSystemId).toString();
			String systemRootUrl = new File("").toURL().toString();
			// Try relative to resource base if currently in system root.
			if (givenUrl.startsWith(systemRootUrl)) {
				resourcePath = givenUrl.substring(systemRootUrl.length());
			}
		}
		// 无法通过URL的方式获取,(可以通过URL获取的时候,他就自动联网获取了,但是如果你
		// 配置了自己资源方式,那么就会出异常),
		catch (Exception ex) {
			// Typically a MalformedURLException or AccessControlException.
			if (logger.isDebugEnabled()) {
				logger.debug("Could not resolve XML entity [" + systemId + "] against system root URL", ex);
			}
			// No URL (or no resolvable URL) -> try relative to resource base.
			// 无法通过URL方式解析,还原为原始的 systemId
			resourcePath = systemId;
		}
		if (resourcePath != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Trying to locate XML entity [" + systemId + "] as resource [" + resourcePath + "]");
			}
			// 通过 resourceLoader 来加载资源
			Resource resource = this.resourceLoader.getResource(resourcePath);
			source = new InputSource(resource.getInputStream());
			source.setPublicId(publicId);
			source.setSystemId(systemId);
			if (logger.isDebugEnabled()) {
				logger.debug("Found XML entity [" + systemId + "]: " + resource);
			}
		}
	}
	return source;

}

 

org.springframework.beans.factory.xml.DelegatingEntityResolver#resolveEntity

public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
	if (systemId != null) {
		// 如果是 dtd 实体
		if (systemId.endsWith(DTD_SUFFIX)) {
			return this.dtdResolver.resolveEntity(publicId, systemId);
		}
		// 如果是 xsd 实体
		else if (systemId.endsWith(XSD_SUFFIX)) {
			return this.schemaResolver.resolveEntity(publicId, systemId);
		}
	}
	return null;
}

 

重点关注一下 xsd 实体解决器PluggableSchemaResolver,他的处理方式类似上面的命名空间处理器,命名空间处理器配置文件默认是 META-INF/spring.handlers ,外部实体的配置文件默认是 META-INF/spring.schemas

 

下面我们找到 spring-bean-3.1.2.RELEASE.jar (我使用的是spring 3.1.2,并且每个包都是单独引入的)包中的 META-INF/spring. schemas,了解里面的配置内容:

 

http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.1.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.1.xsd
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
http\://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.1.xsd

 

下面是处理过程

public InputSource resolveEntity(String publicId, String systemId) throws IOException {
	if (logger.isTraceEnabled()) {
		logger.trace("Trying to resolve XML entity with public id [" + publicId +
				"] and system id [" + systemId + "]");
	}

	if (systemId != null) {
		String resourceLocation = getSchemaMappings().get(systemId);
		if (resourceLocation != null) {
			Resource resource = new ClassPathResource(resourceLocation, this.classLoader);
			try {
				InputSource source = new InputSource(resource.getInputStream());
				source.setPublicId(publicId);
				source.setSystemId(systemId);
				if (logger.isDebugEnabled()) {
					logger.debug("Found XML schema [" + systemId + "] in classpath: " + resourceLocation);
				}
				return source;
			}
			catch (FileNotFoundException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Couldn't find XML schema [" + systemId + "]: " + resource, ex);
				}
			}
		}
	}
	return null;
}

/**
 * Load the specified schema mappings lazily.
 */
private Map<String, String> getSchemaMappings() {
	if (this.schemaMappings == null) {
		synchronized (this) {
			if (this.schemaMappings == null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Loading schema mappings from [" + this.schemaMappingsLocation + "]");
				}
				try {
					Properties mappings =
							PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);
					if (logger.isDebugEnabled()) {
						logger.debug("Loaded schema mappings: " + mappings);
					}
					Map<String, String> schemaMappings = new ConcurrentHashMap<String, String>();
					CollectionUtils.mergePropertiesIntoMap(mappings, schemaMappings);
					this.schemaMappings = schemaMappings;
				}
				catch (IOException ex) {
					throw new IllegalStateException(
							"Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex);
				}
			}
		}
	}
	return this.schemaMappings;

}

 

 至此上面的问比较好回答了。这个过程中我们是围绕两个问题作为线索来分析的,一个是解析 Bean 定义文件的正确性验证(schema对应的xsd或者dtd的文件解析方式),一个是自定义的命名空间解析器(命名空间配置处理器的映射配置)。

分享到:
评论

相关推荐

    Spring-ioc-jar

    5. **生命周期管理**: Spring容器还提供了对Bean生命周期的管理,包括初始化回调、销毁回调、Bean的后处理器等机制,允许开发者在特定阶段插入自定义逻辑。 6. **AOP代理**: Spring的IOC容器与AOP框架紧密集成,...

    撸一撸Spring Framework-IoC-BeanDefinition(csdn)————程序.pdf

    `load`方法进一步委托给`XmlBeanDefinitionReader`,该类负责从XML资源中解析BeanDefinition。`XmlBeanDefinitionReader`内部使用了`DefaultBeanDefinitionDocumentReader`,这是一个XML文档读取器,它会递归地处理...

    Spring IOC设计原理解析.docx

    - `DocumentLoader`将XML转换为DOM对象,`XmlBeanDefinitionReader`解析Bean定义。 - `DefaultBeanDefinitionDocumentReader`解析DOM中的Bean定义元素。 - `BeanDefinitionParserDelegate`解析&lt;bean&gt;和等元素,...

    Spring学习笔记(11)----自定义属性编辑器

    通过理解和熟练运用自定义属性编辑器,开发者可以更好地利用Spring的IoC功能,提高代码的灵活性和可扩展性。在阅读“Spring学习笔记(11)----自定义属性编辑器”这篇博文时,读者将深入了解这个概念,并学会如何在...

    spring ioc

    在提供的 "src" 压缩包文件中,如果包含的是 Spring 框架的源码,那么我们可以详细研究每个类和方法,了解 Spring 如何处理 Bean 的生命周期、解析配置、创建依赖关系等。这将帮助我们成为 Spring 框架的专家,更...

    spring-security-web源码所需jar包

    《Spring Security Web源码解析与相关依赖》 Spring Security Web是Spring Security框架的重要组成部分,它主要负责Web应用程序的安全性,包括认证和授权等核心功能。本文将深入探讨Spring Security Web的源码,并...

    spring-framework-master

    《Spring框架深度解析》 Spring Framework,作为Java开发领域中的基石,是企业级应用开发的首选框架。"spring-framework-master"这一压缩包包含了Spring框架的完整源码,为我们提供了深入理解这一强大工具的绝佳...

    Spring IoC源码深度剖析开源架构源码2021.pdf

    标题《Spring IoC源码深度剖析开源架构源码2021.pdf》和描述《Spring IoC源码深度剖析开源架构源码2021.pdf》表明该文档主要面向于分析Spring框架中控制反转(IoC)容器的核心源码,解析和理解其内部的工作机制及...

    spring ioc和aop原理流程图(详细)

    5. **Bean的生命周期**:Spring容器对Bean的生命周期进行管理,包括初始化、正常使用、销毁等阶段,开发者可以自定义生命周期回调方法以进行特定操作。 ### Spring AOP **面向切面编程(AOP)** 是Spring的另一个...

    模拟Spring的IOC

    **模拟Spring的IOC** ...这不仅有助于理解Spring的工作原理,也为我们自定义轻量级框架提供了可能。在实际开发中,了解并掌握Spring的IOC机制能够帮助我们更好地设计和组织代码,提升代码的灵活性和可维护性。

    以注解方式模拟Spring IoC AOP

    模拟Spring的IoC,我们可以创建一个简单的容器类,其中包含一个Map来存储bean。然后使用注解处理器扫描带有特定注解(如`@Component`)的类,并将它们注册到容器中。当需要注入依赖时,容器可以解析注解并自动装配。...

    手写一个SpringIoc容器

    本项目"手写一个SpringIoc容器"旨在模仿Spring的IOC(Inversion of Control,控制反转)功能,帮助开发者深入理解Spring的工作原理,提升对依赖注入(Dependency Injection)模式的认识。 在实现自定义的Spring IOC...

    spring ioc+mvc代码

    《深入理解Spring IOC+MVC:从零构建自己的框架》 在软件开发领域,Spring框架因其强大的功能和灵活性而备受推崇,特别是其核心的控制反转(IOC)和模型视图控制器(MVC)设计模式。本文将通过分析并实现一个简化版...

    Spring-IoC-Learning:手动实现IoC容器

    Spring的依赖注入(Inversion of Control,IoC)是其核心特性之一,它极大地简化了Java应用程序的开发和维护。这个项目"Spring-IoC-Learning:手动实现IoC容器"旨在通过实践来深入理解Spring IoC的工作原理。在本文中...

    Spring-IOC丐版

    Spring允许我们自定义Bean的初始化方法和销毁方法。 4. **Scope**: Bean的作用域,比如单例(Singleton)、原型(Prototype)等,决定了Bean的实例化策略。 5. **Bean的查找和引用**: 容器提供了获取Bean实例的...

    spring IOC学习源码

    通过分析这些源码,我们可以了解到Spring是如何解析bean定义、如何进行依赖查找和注入,以及如何管理bean的完整生命周期。这对于提升Spring应用的开发和优化能力具有重要意义。 总的来说,Spring IOC的学习涉及了...

    第四章 在Ioc容器中装配Bean

    Spring的IoC容器是一个高度可扩展的框架,允许开发者实现自己的装配机制,自定义Bean的解析、创建、装配过程。 综上所述,本章通过介绍IoC容器的概念、Bean的定义、依赖注入、作用域、生命周期管理以及自动装配等...

    深入解析Spring IoC:源码与实践指南

    在深入解析Spring IoC的过程中,我们首先关注的是其核心组件——IoC(Inversion of Control,控制反转)机制。Spring框架通过IoC容器管理应用程序的组件,实现了依赖注入,使得对象之间的依赖关系由容器负责建立和...

    spring-IOC代码

    **Spring IOC(控制反转)深度解析** 在Java开发领域,Spring框架因其强大的功能和灵活性而备受推崇,其中的核心概念就是IOC(Inversion of Control,控制反转)。本文将深入探讨Spring IOC,以及它如何通过容器来...

    SpringIOC原理实现

    在这个例子中,我们将通过导入Excel数据来深入理解Spring IOC的工作原理,并探讨如何自定义实现。 1. **控制反转**:在传统的编程模式中,我们经常手动创建和管理对象,但在Spring中,对象的创建、初始化、依赖关系...

Global site tag (gtag.js) - Google Analytics