Spring2.0后,可以基于spring的schema来扩展xml format,加入自定义的schema元素。
步骤如下:
1.定义xml schema描述个性化的元素信息
2.编写自定义的NamespaceHandler
3.编写一个或多个BeanDefinitionParser来解析Xml元素
4.注册成为spring的artifacts
demo可以参考http://static.springsource.org/spring/docs/2.5.x/reference/extensible-xml.html
说明:
spring.schema定义uri对应的xsd的位置,基于classpath
spring.handlers定义uri对应的解析类
spring.schema基本思路:
1.spring在解析XML配置文件时,通过DelegatingEntityResolver来解析xml中的元素信息,包括header信息.
初始化:
调用PluggableSchemaResolver:
2.PluggableSchemaResolver来解析指定的资源
调用工具类加载资源信息
3.PropertiesLoaderUtils
根据classloader加载所有的资源,注意因为不同的jar包的资源名字可能相同,所以使用的是classloader的getResources接口:
4.根据xml中的自定义元素根据指定的xsd类型信息来验证。
spring.handles基本思路:
1.spring 在解析xml元素后,要通过XmlBeanDefinitionReader转换成Bean Definition信息。
1.1将前面提到的DelegatingEntityResolver(PluggableSchemaResolver)放到DocumentLoader中
1.2注册bean definition到Dom中
2.调用DefaultDocumentLoader,将前面提到的DelegatingEntityResolver(PluggableSchemaResolver)放到DocumentLoader中
3. 注册bean definition到Dom中,在遍历xml元素,如果是子定义的元素,调用spring.handlers定义的解析类来解析
4.剩下就是ApplicationContext根据解析后的Bean Definition来构造Bean的实例了。
步骤如下:
1.定义xml schema描述个性化的元素信息
2.编写自定义的NamespaceHandler
3.编写一个或多个BeanDefinitionParser来解析Xml元素
4.注册成为spring的artifacts
demo可以参考http://static.springsource.org/spring/docs/2.5.x/reference/extensible-xml.html
说明:
spring.schema定义uri对应的xsd的位置,基于classpath
spring.handlers定义uri对应的解析类
spring.schema基本思路:
1.spring在解析XML配置文件时,通过DelegatingEntityResolver来解析xml中的元素信息,包括header信息.
初始化:
public DelegatingEntityResolver(ClassLoader classLoader) {
this.dtdResolver = new BeansDtdResolver();
this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
调用PluggableSchemaResolver:
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
if (systemId != null) {
if (systemId.endsWith(DTD_SUFFIX)) {
return this.dtdResolver.resolveEntity(publicId, systemId);
}
else if (systemId.endsWith(XSD_SUFFIX)) {
return this.schemaResolver.resolveEntity(publicId, systemId);
}
}
return null;
}
2.PluggableSchemaResolver来解析指定的资源
public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
调用工具类加载资源信息
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;
}
3.PropertiesLoaderUtils
根据classloader加载所有的资源,注意因为不同的jar包的资源名字可能相同,所以使用的是classloader的getResources接口:
public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException {
Assert.notNull(resourceName, "Resource name must not be null");
ClassLoader clToUse = classLoader;
if (clToUse == null) {
clToUse = ClassUtils.getDefaultClassLoader();
}
Properties properties = new Properties();
Enumeration urls = clToUse.getResources(resourceName);
while (urls.hasMoreElements()) {
URL url = (URL) urls.nextElement();
InputStream is = null;
try {
URLConnection con = url.openConnection();
con.setUseCaches(false);
is = con.getInputStream();
properties.load(is);
}
finally {
if (is != null) {
is.close();
}
}
}
return properties;
}
4.根据xml中的自定义元素根据指定的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;
}
spring.handles基本思路:
1.spring 在解析xml元素后,要通过XmlBeanDefinitionReader转换成Bean Definition信息。
1.1将前面提到的DelegatingEntityResolver(PluggableSchemaResolver)放到DocumentLoader中
1.2注册bean definition到Dom中
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
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.调用DefaultDocumentLoader,将前面提到的DelegatingEntityResolver(PluggableSchemaResolver)放到DocumentLoader中
protected DocumentBuilder createDocumentBuilder(
DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)
throws ParserConfigurationException {
DocumentBuilder docBuilder = factory.newDocumentBuilder();
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}
3. 注册bean definition到Dom中,在遍历xml元素,如果是子定义的元素,调用spring.handlers定义的解析类来解析
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// Read document based on new BeanDefinitionDocumentReader SPI.
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
protected XmlReaderContext createReaderContext(Resource resource) {
if (this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
}
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, this.namespaceHandlerResolver);
}
4.剩下就是ApplicationContext根据解析后的Bean Definition来构造Bean的实例了。