基于schema的spring xml namespace扩展

来源:互联网 发布:excel 数据分析 知乎 编辑:程序博客网 时间:2024/04/30 15:40

基于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中

viewplain

 

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的实例了。

 


原创粉丝点击