基于schema的spring xml namespace扩展
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信息.
初始化:
public DelegatingEntityResolver(ClassLoaderclassLoader) {
this.dtdResolver= new BeansDtdResolver();
this.schemaResolver= new PluggableSchemaResolver(classLoader);
}
调用PluggableSchemaResolver:
public InputSource resolveEntity(StringpublicId, String systemId) throws SAXException, IOException {
if(systemId != null) {
if(systemId.endsWith(DTD_SUFFIX)) {
returnthis.dtdResolver.resolveEntity(publicId, systemId);
}
elseif (systemId.endsWith(XSD_SUFFIX)) {
returnthis.schemaResolver.resolveEntity(publicId, systemId);
}
}
returnnull;
}
2.PluggableSchemaResolver来解析指定的资源
public static final StringDEFAULT_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("Loadingschema mappings from [" + this.schemaMappingsLocation + "]");
}
try{
Propertiesmappings =
PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation,this.classLoader);
if(logger.isDebugEnabled()) {
logger.debug("Loadedschema mappings: " + mappings);
}
Map<String,String> schemaMappings = new ConcurrentHashMap<String, String>();
CollectionUtils.mergePropertiesIntoMap(mappings,schemaMappings);
this.schemaMappings= schemaMappings;
}
catch(IOException ex) {
thrownew IllegalStateException(
"Unableto load schema mappings from location [" + this.schemaMappingsLocation +"]", ex);
}
}
}
}
returnthis.schemaMappings;
}
3.PropertiesLoaderUtils
根据classloader加载所有的资源,注意因为不同的jar包的资源名字可能相同,所以使用的是classloader的getResources接口:
public static PropertiesloadAllProperties(String resourceName, ClassLoader classLoader) throwsIOException {
Assert.notNull(resourceName,"Resource name must not be null");
ClassLoaderclToUse = classLoader;
if(clToUse == null) {
clToUse= ClassUtils.getDefaultClassLoader();
}
Propertiesproperties = new Properties();
Enumerationurls = clToUse.getResources(resourceName);
while(urls.hasMoreElements()) {
URLurl = (URL) urls.nextElement();
InputStreamis = null;
try{
URLConnectioncon = url.openConnection();
con.setUseCaches(false);
is= con.getInputStream();
properties.load(is);
}
finally{
if(is != null) {
is.close();
}
}
}
returnproperties;
}
4.根据xml中的自定义元素根据指定的xsd类型信息来验证。
public InputSource resolveEntity(StringpublicId, String systemId) throws IOException {
if(logger.isTraceEnabled()) {
logger.trace("Tryingto resolve XML entity with public id [" + publicId +
"]and system id [" + systemId + "]");
}
if(systemId != null) {
StringresourceLocation = getSchemaMappings().get(systemId);
if(resourceLocation != null) {
Resourceresource = new ClassPathResource(resourceLocation, this.classLoader);
try{
InputSourcesource = new InputSource(resource.getInputStream());
source.setPublicId(publicId);
source.setSystemId(systemId);
if(logger.isDebugEnabled()) {
logger.debug("FoundXML schema [" + systemId + "] in classpath: " +resourceLocation);
}
returnsource;
}
catch(FileNotFoundException ex) {
if(logger.isDebugEnabled()) {
logger.debug("Couldn'tfind XML schema [" + systemId + "]: " + resource, ex);
}
}
}
}
returnnull;
}
spring.handles基本思路:
1.spring 在解析xml元素后,要通过XmlBeanDefinitionReader转换成Bean Definition信息。
1.1将前面提到的DelegatingEntityResolver(PluggableSchemaResolver)放到DocumentLoader中
1.2注册bean definition到Dom中
protected int doLoadBeanDefinitions(InputSourceinputSource, Resource resource)
throwsBeanDefinitionStoreException {
try {
intvalidationMode = getValidationModeForResource(resource);
Documentdoc = this.documentLoader.loadDocument(
inputSource,getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
returnregisterBeanDefinitions(doc, resource);
}
catch(BeanDefinitionStoreException ex) {
throw ex;
}
catch(SAXParseException ex) {
throw newXmlBeanDefinitionStoreException(resource.getDescription(),
"Line" + ex.getLineNumber() + " in XML document from " + resource +" is invalid", ex);
}
catch(SAXException ex) {
throw newXmlBeanDefinitionStoreException(resource.getDescription(),
"XMLdocument from " + resource + " is invalid", ex);
}
catch(ParserConfigurationException ex) {
throw newBeanDefinitionStoreException(resource.getDescription(),
"Parserconfiguration exception parsing XML from " + resource, ex);
}
catch(IOException ex) {
throw newBeanDefinitionStoreException(resource.getDescription(),
"IOExceptionparsing XML document from " + resource, ex);
}
catch(Throwable ex) {
throw newBeanDefinitionStoreException(resource.getDescription(),
"Unexpectedexception parsing XML document from " + resource, ex);
}
}
2.调用DefaultDocumentLoader,将前面提到的DelegatingEntityResolver(PluggableSchemaResolver)放到DocumentLoader中
protected DocumentBuilder createDocumentBuilder(
DocumentBuilderFactoryfactory, EntityResolver entityResolver, ErrorHandler errorHandler)
throwsParserConfigurationException {
DocumentBuilderdocBuilder = factory.newDocumentBuilder();
if(entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
if(errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
returndocBuilder;
}
3. 注册beandefinition到Dom中,在遍历xml元素,如果是子定义的元素,调用spring.handlers定义的解析类来解析
public int registerBeanDefinitions(Document doc, Resourceresource) throws BeanDefinitionStoreException {
// Readdocument based on new BeanDefinitionDocumentReader SPI.
BeanDefinitionDocumentReaderdocumentReader = createBeanDefinitionDocumentReader();
intcountBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc,createReaderContext(resource));
returngetRegistry().getBeanDefinitionCount() - countBefore;
}
protectedXmlReaderContext createReaderContext(Resource resource) {
if(this.namespaceHandlerResolver == null) {
this.namespaceHandlerResolver= createDefaultNamespaceHandlerResolver();
}
return newXmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor,this, this.namespaceHandlerResolver);
}
4.剩下就是ApplicationContext根据解析后的Bean Definition来构造Bean的实例了。