是时候进入开发Spring-WS服务的最后阶段了,需要完成的工作包括配置Spring应用context和端点bean以及一组Spring-WS所需的基础bean。
Spring-WS是基于Spring MVC的。在Spring MVC中所有的请求都由DispatcherServlet处理。它负责分发那些被传递到处理请求的控制器的请求。Spring-WS提供了MessageDispatchrServlet,它是DispatcherServlet的子类,负责分发到Spring-WS端点的SOAP请求。MessageDispatchrServlet在web.xml文件中配置:
<servlet>
<servlet-name>poker</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>poker</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
在Spring-WS的前端你只需要配置MessageDispatcherServlet。除此之外,我们还需要配置一组bean。
1. Spring-WS:概览
在配置那些bean之前,先看看它们是什么样的以及之间的相关关系。如图:
下面分别进行解释:
·payloadMapping:映射XML消息到一个合适的端点。本例将使用一个mapping来根据XML消息的根节点查询端点。
·evaluateHandEndpoint:处理接收的XML消息的端点。
·marshaller:evaluateHandEndpoint可以将XML消息作为DOM或JDOM元素,或SAX事件handler来处理。相反,marshaller bean会自动地将XML转化为Java对象。
·pokerHandEvaluator:一个POJO,执行真正的poker hand处理服务,由evaluateHandEndpoint调用。
·endpointExceptionResolver:Spring-WS bean,处理请求时会自动地转化Java异常为对应的SOAP错误。
·poker:为web service提供WSDL,可以手工创建也可以从消息的XSD中自动生成。
下面就分别来看看这些配置,先从消息处理适配器(message handler adapter)开始。
2. 映射消息到端点
客户发送消息时,MessageDispatcherServlet如何知道哪个端点应该处理该消息呢?即使我们只创建一个端点,MessageDispatcherServlet还是有可能被配置多个端点。因此我们需要能够映射消息到相应的端点上。
MessageDispatcherServlet使用端点映射机制来确定哪些端点应该接收到来的XML消息。对于poker hand服务来说,我们可以使用Spring-WS提供的PayloadRootQNameEndpointMapping,例如:
<bean id="payloadMapping"
class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="endpintMap">
<map>
<entry key="{http://www.springinaction.com/poker/schemas}EvaluateHandRequest"
value-ref="evaluateHandEndpoint" />
</map>
</property>
</bean>
PayloadRootQNameEndpointMapping映射SOAP消息的方法是检查消息payload的QName并从端点映射列表(通过endpointMap属性配置)中查询出适合的端点然后映射之。
在这个例子中,消息的根节点元素是<EvaluateHandRequest>,并带有一个命名空间URI:http://www.springinaction.com/poker/schemas,因此QName就是{http://www.springinaction.com/poker/schemas} EvaluateHandRequest。我们将此QName映射到一个名为evaluateHandEndpoint的bean上,而evaluateHandEndpoint就是我们前面创建的端点的实现。
3. 绑定服务端点
我们现在需要绑定这个端点。如果你使用基于JDOM的端点,配置方法如下:
<bean id="evaluateHandEndpoint"
class="com.springinaction.poker.webservice.EvaluateHandJDomEndpoint">
<property name="pokerHandEvaluator" ref="pokerHandEvaluator" />
</bean>
我们只需要配置一个属性,即pokerHandEvaluator。EvaluateHandEndpoint不会执行真正的服务操作,它只是委托给PokerHandEvaluator的一个实现来完成。所以,pokerHandEvaluator bean应当如此配置:
<bean id="pokerHandEvaluator"
class="com.springinaction.poker.PokerHandEvaluatorImpl" />
如果你使用的是EvaluateHandMarshallingEndpoint,那么你就需要如下配置:
<bean id="evaluateHandEndpoint"
class="com.springinaction.poker.webservice.EvaluateHandMarshallingEndpoint">
<property name="marshaller" ref="marshaller" />
<property name="unmarshaller" ref="marshaller" />
<property name="pokerHandEvaluator" ref="pokerHandEvaluator" />
</bean>
值得注意的是,pokerHandEvaluator属性引用了一个PokerHandEvaluatorImpl对象。但是marshaling端点还需要配置marshaller和unmarshaller属性,这里我们都引用了相同的marshaller bean。
4. 配置消息marshaler
实现对象和XML消息转换的关键在于OXM(object-XML mapping)。Spring-OXM作为Spring-WS的一个子项目在通用的OXM解决方案(如JAXB和Castor XML)之上提供了一层抽象层。
Spring OXM核心元素是Marshaller和Unmarshaller接口。实现Marshaller接口可以实现从Java对象到XML消息的转换;实现Unmarshaller接口可实现从XML消息到java对象的转换。AbstractMarshallingPayloadEndpoint即发挥了它们的优势。当AbstractMarshallingPayloadEndpoint接收到一条消息时,它会将消息传递给Unmarshaller来转化XML消息到java对象,并将此对象传递个invokeInternal方法。当invokeInternal方法完成后,返回的对象被传递给Marshaller将对象转换成XML消息并返回给客户。
你不需要自己实现Marshaller和Unmarshaller,Spring-OXM提供了一些具体的实现,如CastorMarshaller和Jaxb1Marshaller等。它们同时实现了Marshaller和UnMarshaller接口,从而可以提供一应俱全的功能。
你可以任意选择这些实现类,但是对Xstream而言,它是有条件地支持XML命名空间的,因为XML命名空间对于定义web service类型时必须得,所以Xstream不适用于web service,但却适用于XML序列化。
对于poker hand evaluator服务而言,我们选择使用Castor XML,配置文件如下:
<bean id="marshaller"
class="org.springframework.oxm.castor.CastorMarshaller">
<property name="mappingLocation"
value="classpath:mapping.xml" />
</bean>
不用任何配置Castor XML即可完成一些基本的XML marshaling,但是为了完成更复杂的工作,我们需要使用Castor XML映射文件。mappingLocation属性即指定了该映射文件的位置。
配置完一个端点的映射bean,端点实现bean和XML marshaling bean之后,我们就完成了poker hand evaluator服务的大部分工作,但还存在着一些bean可以让这个web service更加完整。为使我们的web service更加健壮,我们要声明一个映射Java异常到SOAP错误的bean。