首页 > 代码库 > spring中解析xml

spring中解析xml

解析xml有SAX,Stax,dom等方式,那么spring中是如何解析xml文件的呢?

Document doc = this.documentLoader.loadDocument(                    inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());

spring中采用的DOM的方式,所要做的一切就是得到org.w3c.dom.Document对象

上面的documentLoader是DefaultDocumentLoader对象

/* * Copyright 2002-2007 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.beans.factory.xml;import javax.xml.parsers.DocumentBuilder;import javax.xml.parsers.DocumentBuilderFactory;import javax.xml.parsers.ParserConfigurationException;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.util.xml.XmlValidationModeDetector;import org.w3c.dom.Document;import org.xml.sax.EntityResolver;import org.xml.sax.ErrorHandler;import org.xml.sax.InputSource;/** * Spring‘s default {@link DocumentLoader} implementation. * * <p>Simply loads {@link Document documents} using the standard JAXP-configured * XML parser. If you want to change the {@link DocumentBuilder} that is used to * load documents, then one strategy is to define a corresponding Java system property * when starting your JVM. For example, to use the Oracle {@link DocumentBuilder}, * you might start your application like as follows: * * <pre code="class">java -Djavax.xml.parsers.DocumentBuilderFactory=oracle.xml.jaxp.JXDocumentBuilderFactory MyMainClass</pre> * * @author Rob Harrop * @author Juergen Hoeller * @since 2.0 */public class DefaultDocumentLoader implements DocumentLoader {    /**     * JAXP attribute used to configure the schema language for validation.     */    private static final String SCHEMA_LANGUAGE_ATTRIBUTE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";    /**     * JAXP attribute value indicating the XSD schema language.     */    private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";    private static final Log logger = LogFactory.getLog(DefaultDocumentLoader.class);    /**     * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured     * XML parser.     */    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);        if (logger.isDebugEnabled()) {            logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");        }        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);        return builder.parse(inputSource);    }    /**     * Create the {@link DocumentBuilderFactory} instance.     * @param validationMode the type of validation: {@link XmlValidationModeDetector#VALIDATION_DTD DTD}     * or {@link XmlValidationModeDetector#VALIDATION_XSD XSD})     * @param namespaceAware whether the returned factory is to provide support for XML namespaces     * @return the JAXP DocumentBuilderFactory     * @throws ParserConfigurationException if we failed to build a proper DocumentBuilderFactory     */    protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)            throws ParserConfigurationException {        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();        factory.setNamespaceAware(namespaceAware);        if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {            factory.setValidating(true);            if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {                // Enforce namespace aware for XSD...                factory.setNamespaceAware(true);                try {                    factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);                }                catch (IllegalArgumentException ex) {                    ParserConfigurationException pcex = new ParserConfigurationException(                            "Unable to validate using XSD: Your JAXP provider [" + factory +                            "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +                            "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");                    pcex.initCause(ex);                    throw pcex;                }            }        }        return factory;    }    /**     * Create a JAXP DocumentBuilder that this bean definition reader     * will use for parsing XML documents. Can be overridden in subclasses,     * adding further initialization of the builder.     * @param factory the JAXP DocumentBuilderFactory that the DocumentBuilder     * should be created with     * @param entityResolver the SAX EntityResolver to use     * @param errorHandler the SAX ErrorHandler to use     * @return the JAXP DocumentBuilder     * @throws ParserConfigurationException if thrown by JAXP methods     */    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;    }}

loadDocument中有一个参数entityResolver,类型是EntityResolver,这也没有什么,DocumentBuilder本来就可以设置setEntityResolver,那么现在这个entityResolver是什么对象呢?是

DelegatingEntityResolver ,

 

 

 

/* * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.beans.factory.xml;import java.io.IOException;import org.springframework.util.Assert;import org.xml.sax.EntityResolver;import org.xml.sax.InputSource;import org.xml.sax.SAXException;/** * {@link EntityResolver} implementation that delegates to a {@link BeansDtdResolver} * and a {@link PluggableSchemaResolver} for DTDs and XML schemas, respectively. * * @author Rob Harrop * @author Juergen Hoeller * @author Rick Evans * @since 2.0 * @see BeansDtdResolver * @see PluggableSchemaResolver */public class DelegatingEntityResolver implements EntityResolver {    /** Suffix for DTD files */    public static final String DTD_SUFFIX = ".dtd";    /** Suffix for schema definition files */    public static final String XSD_SUFFIX = ".xsd";    private final EntityResolver dtdResolver;    private final EntityResolver schemaResolver;    /**     * Create a new DelegatingEntityResolver that delegates to     * a default {@link BeansDtdResolver} and a default {@link PluggableSchemaResolver}.     * <p>Configures the {@link PluggableSchemaResolver} with the supplied     * {@link ClassLoader}.     * @param classLoader the ClassLoader to use for loading     * (can be {@code null}) to use the default ClassLoader)     */    public DelegatingEntityResolver(ClassLoader classLoader) {        this.dtdResolver = new BeansDtdResolver();        this.schemaResolver = new PluggableSchemaResolver(classLoader);    }    /**     * Create a new DelegatingEntityResolver that delegates to     * the given {@link EntityResolver EntityResolvers}.     * @param dtdResolver the EntityResolver to resolve DTDs with     * @param schemaResolver the EntityResolver to resolve XML schemas with     */    public DelegatingEntityResolver(EntityResolver dtdResolver, EntityResolver schemaResolver) {        Assert.notNull(dtdResolver, "‘dtdResolver‘ is required");        Assert.notNull(schemaResolver, "‘schemaResolver‘ is required");        this.dtdResolver = dtdResolver;        this.schemaResolver = schemaResolver;    }    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;    }    @Override    public String toString() {        return "EntityResolver delegating " + XSD_SUFFIX + " to " + this.schemaResolver +                " and " + DTD_SUFFIX + " to " + this.dtdResolver;    }}

在DocumentBuilder调用parse方法的时候会调用EntityResolver(这里是DelegatingEntityResolver)的resolveEntity方法:

    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;    }

请注意resolveEntity方法会根据systemId来返回不同的对象,systemId即是xsi:schemaLocation种的值

 

/* * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.beans.factory.xml;import java.io.FileNotFoundException;import java.io.IOException;import java.util.Map;import java.util.Properties;import java.util.concurrent.ConcurrentHashMap;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.core.io.ClassPathResource;import org.springframework.core.io.Resource;import org.springframework.core.io.support.PropertiesLoaderUtils;import org.springframework.util.Assert;import org.springframework.util.CollectionUtils;import org.xml.sax.EntityResolver;import org.xml.sax.InputSource;/** * {@link EntityResolver} implementation that attempts to resolve schema URLs into * local {@link ClassPathResource classpath resources} using a set of mappings files. * * <p>By default, this class will look for mapping files in the classpath using the pattern: * {@code META-INF/spring.schemas} allowing for multiple files to exist on the * classpath at any one time. * * The format of {@code META-INF/spring.schemas} is a properties * file where each line should be of the form {@code systemId=schema-location} * where {@code schema-location} should also be a schema file in the classpath. * Since systemId is commonly a URL, one must be careful to escape any ‘:‘ characters * which are treated as delimiters in properties files. * * <p>The pattern for the mapping files can be overidden using the * {@link #PluggableSchemaResolver(ClassLoader, String)} constructor * * @author Rob Harrop * @author Juergen Hoeller * @since 2.0 */public class PluggableSchemaResolver implements EntityResolver {    /**     * The location of the file that defines schema mappings.     * Can be present in multiple JAR files.     */    public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";    private static final Log logger = LogFactory.getLog(PluggableSchemaResolver.class);    private final ClassLoader classLoader;    private final String schemaMappingsLocation;    /** Stores the mapping of schema URL -> local schema path */    private volatile Map<String, String> schemaMappings;    /**     * Loads the schema URL -> schema file location mappings using the default     * mapping file pattern "META-INF/spring.schemas".     * @param classLoader the ClassLoader to use for loading     * (can be {@code null}) to use the default ClassLoader)     * @see PropertiesLoaderUtils#loadAllProperties(String, ClassLoader)     */    public PluggableSchemaResolver(ClassLoader classLoader) {        this.classLoader = classLoader;        this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION;    }    /**     * Loads the schema URL -> schema file location mappings using the given     * mapping file pattern.     * @param classLoader the ClassLoader to use for loading     * (can be {@code null}) to use the default ClassLoader)     * @param schemaMappingsLocation the location of the file that defines schema mappings     * (must not be empty)     * @see PropertiesLoaderUtils#loadAllProperties(String, ClassLoader)     */    public PluggableSchemaResolver(ClassLoader classLoader, String schemaMappingsLocation) {        Assert.hasText(schemaMappingsLocation, "‘schemaMappingsLocation‘ must not be empty");        this.classLoader = classLoader;        this.schemaMappingsLocation = schemaMappingsLocation;    }    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>(mappings.size());                        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;    }    @Override    public String toString() {        return "EntityResolver using mappings " + getSchemaMappings();    }}
/* * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.beans.factory.xml;import java.io.IOException;import java.util.Arrays;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.core.io.ClassPathResource;import org.springframework.core.io.Resource;import org.xml.sax.EntityResolver;import org.xml.sax.InputSource;/** * EntityResolver implementation for the Spring beans DTD, * to load the DTD from the Spring class path (or JAR file). * * <p>Fetches "spring-beans-2.0.dtd" from the class path resource * "/org/springframework/beans/factory/xml/spring-beans-2.0.dtd", * no matter whether specified as some local URL that includes "spring-beans" * in the DTD name or as "http://www.springframework.org/dtd/spring-beans-2.0.dtd". * * @author Juergen Hoeller * @author Colin Sampaleanu * @since 04.06.2003 * @see ResourceEntityResolver */public class BeansDtdResolver implements EntityResolver {    private static final String DTD_EXTENSION = ".dtd";    private static final String[] DTD_NAMES = {"spring-beans-2.0", "spring-beans"};    private static final Log logger = LogFactory.getLog(BeansDtdResolver.class);    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 && systemId.endsWith(DTD_EXTENSION)) {            int lastPathSeparator = systemId.lastIndexOf("/");            for (String DTD_NAME : DTD_NAMES) {                int dtdNameStart = systemId.indexOf(DTD_NAME);                if (dtdNameStart > lastPathSeparator) {                    String dtdFile = systemId.substring(dtdNameStart);                    if (logger.isTraceEnabled()) {                        logger.trace("Trying to locate [" + dtdFile + "] in Spring jar");                    }                    try {                        Resource resource = new ClassPathResource(dtdFile, getClass());                        InputSource source = new InputSource(resource.getInputStream());                        source.setPublicId(publicId);                        source.setSystemId(systemId);                        if (logger.isDebugEnabled()) {                            logger.debug("Found beans DTD [" + systemId + "] in classpath: " + dtdFile);                        }                        return source;                    }                    catch (IOException ex) {                        if (logger.isDebugEnabled()) {                            logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in class path", ex);                        }                    }                }            }        }        // Use the default behavior -> download from website or wherever.        return null;    }    @Override    public String toString() {        return "EntityResolver for DTDs " + Arrays.toString(DTD_NAMES);    }}

 

在PluggableSchemaResolver类中会去加载META-INF/spring.schemas

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

然后根据systemId去对象的位置获取对应xsd文件

 

为什么要设置EntityResolver呢?如果xml文件不符合xsd文件,则解析时候会报错,spring中对这种错误的情况只是报异常而已

/* * Copyright 2002-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.util.xml;import org.apache.commons.logging.Log;import org.xml.sax.ErrorHandler;import org.xml.sax.SAXException;import org.xml.sax.SAXParseException;/** * Simple {@code org.xml.sax.ErrorHandler} implementation: * logs warnings using the given Commons Logging logger instance, * and rethrows errors to discontinue the XML transformation. * * @author Juergen Hoeller * @since 1.2 */public class SimpleSaxErrorHandler implements ErrorHandler {    private final Log logger;    /**     * Create a new SimpleSaxErrorHandler for the given     * Commons Logging logger instance.     */    public SimpleSaxErrorHandler(Log logger) {        this.logger = logger;    }    public void warning(SAXParseException ex) throws SAXException {        logger.warn("Ignored XML validation warning", ex);    }    public void error(SAXParseException ex) throws SAXException {        throw ex;    }    public void fatalError(SAXParseException ex) throws SAXException {        throw ex;    }}

 

spring中解析xml