领先一步
VMware 提供培训和认证,助您加速进步。
了解更多继我关于WS-DuckTyping的帖子之后,我认为展示Spring Web Services为XPath提供的支持会很有趣。其中一些功能现在可用,但大部分将包含在本月晚些时候发布的RC1版本中。在整篇文章中,我将使用Rusty Harold的《Effective XML》中第35项定义的contacts xml文件。
最近,我添加了XPathExpressionFactoryBean,以便更轻松地将XPath表达式注入到您的bean中,例如
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="myEndpoint" class="com.mycompany.ws.MyEndpoint">
<constructor-arg ref="nameExpression"/>
</bean>
<bean id="nameExpression" class="org.springframework.xml.xpath.XPathExpressionFactoryBean">
<property name="expression" value="/Contacts/Contact/Name"/>
</bean>
</beans>
为简单起见,此表达式未使用命名空间,但我们可以使用工厂 Bean 的 namespaces 属性来设置它们。
可以在代码中像这样使用该表达式:
public class MyEndpoint extends AbstractDomPayloadEndpoint {
private XPathExpression nameExpression;
public MyEndpoint(XPathExpression nameExpression) {
this.nameExpression = nameExpression;
}
// Gets invoked for every incoming request
protected Element invokeInternal(Element requestElement, Document responseDocument) throws Exception {
String name = nameExpression.evaluateAsString(requestElement);
// do something with name
return null; // no response
}
}
最近 XPathExpression 的一个新功能是 NodeMapper。如果我们把表达式改为 /Contacts/Contact,就可以像这样使用它:
public class MyEndpoint extends AbstractDomPayloadEndpoint {
private XPathExpression nameExpression;
public MyEndpoint(XPathExpression nameExpression) {
this.nameExpression = nameExpression;
}
protected Element invokeInternal(Element requestElement, Document responseDocument) throws Exception {
List contacts = nameExpression.evaluate(requestElement,
new NodeMapper() {
public Object mapNode(Node node, int nodeNum) throws DOMException {
Element contactElement = (Element) node;
Element nameElement = (Element) contactElement.getElementsByTagName("Name").item(0);
Element phoneElement = (Element) contactElement.getElementsByTagName("Phone").item(0);
return new Contact(nameElement.getTextContent(), phoneElement.getTextContent());
}
} );
for (Iterator iterator = contacts.iterator(); iterator.hasNext();) {
Contact contact = (Contact) iterator.next();
System.out.println(contact);
}
return null; // no response
}
}
就像我们使用 Spring JDBC 的 RowMapper 来映射行一样,每个结果节点都使用匿名内部类进行映射。在这种情况下,我们创建一个 Contact 对象,然后稍后打印它。
public class MyEndpoint implements PayloadEndpoint {
private XPathOperations template = new Jaxp13XPathTemplate();
public Source invoke(Source request) throws Exception {
String name = template.evaluateAsString("/Contacts/Contact/Name", request);
// do something with name
return null; // no response
}
}
当然,可以将模板通过构造函数参数或 setter 进行注入。
XPath 模板不使用预编译的 XPath 表达式,因此速度稍慢一些。但是,模板更灵活,因为您可以将其用于多个表达式。它还支持上面描述的 NodeMapper。
@Endpoint
public class MyEndpoint {
@PayloadRoot("Contacts")
public void handleContacts(@XPathParam("/Contacts/Contact/Name")String name) {
// do something with name
}
}
为了将类标记为端点,需要使用 @Endpoint 注解;当收到根元素为 Contact 的 SOAP 请求时,@PayloadRoot 会指示调用 handleContacts 方法。最后,@XPathParam 执行参数绑定。您可以绑定到 XPath 支持的所有数据类型:布尔值、双精度值、字符串、节点和节点列表。当然,将所有表达式放在一个配置文件中会更易于维护,但对于更简单的项目,这是一种可行的方法。
使用这种带注解的端点风格只需要一点点 XML 配置,只需让 Spring-WS 知道端点的存在并使用注解即可。其余的都会自动处理。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"/>
<bean class="org.springframework.ws.server.endpoint.adapter.XPathParamAnnotationMethodEndpointAdapter"/>
<bean id="myEndpoint" class="com.mycompany.ws.MyEndpoint"/>
</beans>
这些功能中的大多数都将包含在本月晚些时候发布的 RC1 版本中(该版本还将包含我们正在编写的更新的参考文档)。然而,此版本有快照可供下载 此处,请尝试一下,并在 论坛 上报告您的反馈。