Spring Integration中如何配置自定义ISOEntities实体解析器?
场景与现有配置
使用Spring Integration执行XML元素提取,相关配置如下:
<bean id="myDmTypeXPathExpression" class="org.springframework.xml.xpath.XPathExpressionFactoryBean"> <property name="expression" value="/root/level0/*[last()]/local-name()"/> </bean> <int-xml:xpath-header-enricher> <int-xml:header name="dmType" xpath-expression-ref="myDmTypeXPathExpression" overwrite="true"/> </int-xml:xpath-header-enricher>
输入XML的DOCTYPE包含本地ISOEntities引用:
<!DOCTYPE mydocument [ <!ENTITY % ISOEntities PUBLIC "ISO 8879-1986//ENTITIES ISO Character Entities 20030531//EN//XML" "ent/ISOEntities"> %ISOEntities; ]>
所有ISOEntities文件存放在本地目录,单元测试已验证实体解析器有效性:
@Test void check_document_parse() throws Exception { // GIVEN Resource xmlResource = loadMyXmlWithISOEntities(); // WHEN DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); documentBuilder.setEntityResolver(loadMyCatalogResolver()); // THEN documentBuilder.parse(xmlResource.getFile()); }
但Spring Integration流程抛出异常:
Caused by: java.io.FileNotFoundException: path/to/my/app/ent/ISOEntities at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1910) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1512) at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:646) at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1301) at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1237) at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.startPE(XMLDTDScannerImpl.java:739) at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.skipSeparator(XMLDTDScannerImpl.java:2108) at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.scanDecls(XMLDTDScannerImpl.java:2071) at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.scanDTDInternalSubset(XMLDTDScannerImpl.java:363) at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.dispatch(XMLDocumentScannerImpl.java:1128)
解决方案
可以为Spring Integration的XML组件配置自定义实体解析器,核心是替换默认的DocumentBuilder或XmlPayloadConverter,具体步骤如下:
1. 定义带有实体解析器的DocumentBuilder Bean
复用单元测试中的实体解析器逻辑,创建自定义的DocumentBuilderFactory和DocumentBuilder:
@Bean public DocumentBuilderFactory documentBuilderFactory() { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); // 根据业务需求调整命名空间配置 return factory; } @Bean public DocumentBuilder documentBuilder(DocumentBuilderFactory factory) throws ParserConfigurationException { DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(loadMyCatalogResolver()); // 加载你已验证的实体解析器 return builder; }
2. 配置XPath Header Enricher使用自定义组件
有两种方式让Spring Integration的XML组件使用自定义的DocumentBuilder:
方式一:通过XmlPayloadConverter注入
创建自定义的DefaultXmlPayloadConverter,关联上面的DocumentBuilder,然后配置给xpath-header-enricher:
<bean id="customXmlConverter" class="org.springframework.integration.xml.DefaultXmlPayloadConverter"> <constructor-arg ref="documentBuilder"/> </bean> <int-xml:xpath-header-enricher xml-converter="customXmlConverter"> <int-xml:header name="dmType" xpath-expression-ref="myDmTypeXPathExpression" overwrite="true"/> </int-xml:xpath-header-enricher>
方式二:为XPathExpressionFactoryBean配置DocumentBuilderFactory
直接给XPathExpressionFactoryBean指定自定义的DocumentBuilderFactory,确保XPath解析XML时使用正确的实体规则:
<bean id="myDmTypeXPathExpression" class="org.springframework.xml.xpath.XPathExpressionFactoryBean"> <property name="expression" value="/root/level0/*[last()]/local-name()"/> <property name="documentBuilderFactory" ref="documentBuilderFactory"/> </bean>
原理说明
Spring Integration的XML组件默认使用的DocumentBuilder未配置实体解析器,解析带有本地实体的XML时,默认解析器会将相对路径当作HTTP URL尝试加载,导致FileNotFoundException。替换为自定义的DocumentBuilder后,会使用你已验证的实体解析器加载本地ISOEntities文件,解决异常问题。
内容的提问来源于stack exchange,提问作者Valeriane

