Cyh的博客

Email:kissyan4916@163.com
posts - 26, comments - 19, trackbacks - 0, articles - 220

笔记之Spring-Web Service

Posted on 2009-02-19 00:40 啥都写点 阅读(2506) 评论(0)  编辑  收藏 所属分类: J2EE


  • 定义服务契约:设计由Web服务处理的样本XML消息。我们将使用这些样本消息创建用于生成WSDL的XML Schema。

    • 数据契约:定义了进入和流出服务的消息。使用XML模式(XSD)定义。XSD允许我们精确地定义消息的内容。我们不仅能够定义消息中的元素,还可以指定这些消息的类型以及对消息中数据的限制。  (小提示:尽管手工编写XSD不是很困难,但是也需要我们做更多的工作,XSD interface工具可以基于内容检查一个或多个XML文件,生成可以验证XML文件的XML模式,见PPT 1 。虽然这样为我们节省了很多工作,但我们不能完全不做工作。这个XSD并不是很好,因为它会假设XML中的数据类型。大多数时候,这些假设没有问题,但是通常情况下,我们还是需要XSD能够更精确一点 )  

      操作契约:定义了服务将要执行的操作。

    编写服务端点:创建类来接收和处理发向Web服务的消息。(消息端点是一个类,用于从客户端接收XML消息,根据消息的内容,调用内部应用程序对象执行实际的工作。Spring-WS定义了几个可以创建消息端点的抽象类。见PPT2)

    • 建立基于JDOM消息的端点

      序列化消息载荷:AbstractorMarshallingPayloadEndpoint与其他Spring-WS抽象端点类不同,它并不是从XML元素中抽取信息,而是通过处理对象获取信息(PPT 3)。见Spring in action P240程序清单9.3 你会发现它比EvaluateHandJDomEndpoint短得多。这是因为EvaluateHandMarshallingEndpoint没有任何在EvaluateHandJDomEndpoint中所必须的XML解析代码。 在这里我们没有看到的是AbstractMarshallingPayloadEndpoint中有一个XML序列化器的引用。当接收到XML消息时,它会在调用invokeInternal()前使用这个序列化器将XML消息变成对象。     Spring-WS的大部分内容是对象-XML映射(OXM)抽象。Spring -WS的OXM提供了几个OXM实现: JAXB(版本1和版本2)、Castor XML、JiBX、XMLBeans、XStream好处:因为它使用了简单对象作为参数,所以可以像其他POJO一样进行单元测试。测试用例可以传递到EvaluateHandRequest对象中,并且做出返回EvaluateHandResponse的断言。

    配置端点和Spring-WS基础结构:将消息端点与可以将所有内容结合在一起的Spring-WS Bean装配起来。

    • Spring-WS是基于Spring MVC的,在Spring MVC中,所有请求都由DispatcherServlet操作,这个Servlet会将请求分配给处理请求的控制器。类似的,Spring-WS会在前台使用MessageDispatcherServlet。它是DispatcherServlet的子类,用于将SOAP请求分配给Spring-WS端点。<servlet>        <servlet-name>spring-ws</servlet-name>           <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>        </servlet>    <servlet-mapping>        <servlet-name>spring-ws</servlet-name>        <url-pattern>/*</url-pattern>    </servlet-mapping>后面会对这个配置进行修改,暂且先作为我们研究Spring-WS的起点

      将消息映射到端点:MessageDispatcherServlet也使用了端点映射来决定由哪个端点接收输入的XML消息。对于扑克评分服务,我们将使用Spring-WS的PayloadRootQNameEndpointMapping,在Spring中配置如下:<bean id="payloadMapping" class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping"><property name="endpointMap">  <map>     <entry key="{http://www.springinaction.com/poker/schemas}EvaluateHandRequest"  value-ref="evaluateHandEndpoint"/>  </map> </property>PayloadRootQNameEndpointMapping首先会检查消息载荷的限定名(QName),接着再通过查询映射列表(通过endpointMap属性配置的)找到输入SOAP消息映射到的端点。

      配置消息序列化器:在对象和XML消息之间进行转换的关键是对象-XML 映射(OXM)。Spring-OXM是Spring-WS的一个子项目,提供对几种常见OXM解决方案的抽象,其中包括JAXB和Castor XML.   Spring-OXM的核心元素是Marshaller和Unmarshaller接口。Marshaller的实现可以根据Java对象生成XML元素。与之相反,Unmarshaller的实现则被用于根据XML元素构建Java对象。  AbstractMarshallingPayloadEndPoint在处理消息时会使用Spring-OXM的序列化器和反序列化器。当AbstractMarshallingPayloadEndPoint接收到消息时,会将这条XML消息交给Unmarshaller,反序列化成一个对象,并传递给invokeInternal()方法。接下来,在invokeInternal()完成处理后,返回的对象则被交给Marshaller,序列化成XML,并传递给客户端。  幸运的是,你不必自己创建Marshaller 和 Unmarshaller的实现。Spring-OXM提供了几个实现。见PPT(4) 我们为扑克评分服务选择了Castor XML.所以,需要按下面的方法在Spring中配置CastorMarshaller:  <bean id="marshaller" class="org.springframework.oxm.castor.CastorMarshaller">       <property  name="mappingLocation"  value="classpath:mapping.xml" /></bean>不需要附加的配置,Castor XML就可以进行一些基本的XML序列化工作。但是,我们的OXM进行的工作比默认Castor XML可以处理的工作要更复杂一些。因此,需要使用一个Castor XML映射文件来配置CastorMarshaller。mappingLoaction 属性指定了Castor XML映射文件的位置。这里,我们将mappingLoaction设置为在应用程序跟classpath中查找名字是mapping.xml的映射文件。  (见spring in action P245 程序清单9.4)

      配置端点异常:我们需要一种方法能够将Web服务或Spring-WS抛出的任何Java异常转换成SOAP错误。为了实现这个目标,Spring-WS提供了SoapFaultMappingExceptionResolver。见PPT5,SoapFaultMappingExceptionResolver可以处理发生在消息处理过程中的任何未捕捉异常,并生成一个相应的SOAP错误返回给客户端。在Spring中的配置:<bean id="endpointExceptionResolver" class="org.springframework.ws.soap.serer.endpoint.SoapFaultMappingExceptionResolver">    <property name="exceptionMappings">       <props>          <prop key="org.springframework.oxm.UnmarshallingFailureException">      SENDER,Invalid message received  </prop>  <prop key="org.springframework.oxm.ValidationFailureException">      SENDER,Invalid message received  </prop>       </props>    </property>    <property name="defaultFault" value="RECEIVER,Server error" /> </bean>   可以为exceptionMapping属性配置一个或多个SOAP错误定义(被映射到Java异常的)。每个<prop>的Key是需要转换成SOAP错误的Java异常,<prop>的值是一个由两部分内容构成的值,第一部分是错误的类型,第二部分是描述错误的字符串。

      提供WSDL文件:你应该注意到我为构成Web服务消息的XML元素选择的名称:EvaluateHandRequest和EvaluateHandResponse。这些名字不是任意选择的。选择他们的目的是利用Spring-WS中的配置惯例,自动为扑克评分服务创建WSDL。   为了完成这个工作,需要配置Spring-WS的DynamicWsdlllDefinition。它是一个特殊的Bean,MessageDispatcherServlet可以使用它从XML模式生成WSDL。这将非常方便,因为我们已经为契约的数据部分定义了一些XML模式。下面如何配置DynamicWsdlllDefinition:

      <bean id="poker" class="org.springframework.ws.wsdl.wsdlll.DynamicWsdlllDefinition">

         <property name="builter">

              <bean class="org.springframework.ws.wsdl.wsdlll.builder.XsdBasedSoapllWsdl4jDefinitionBuilder">

         <property name="schema" value="/PokerTypes.xsd" />

         <property name="portTypeName" value="Poker" />

         <property name="locationUri" value="http://localhost:8080/Poker-WS/services" />

      </bean>

         </property>

      </bean>

      DynamicWsdlllDefinition通过读取XML模式的定义来进行工作,在这里用schema属性指定了PokerTypes.xsd。DynamicWsdlllDefintion会遍历整个模式文件,查找所有名字以Request和Response结尾元素定义。它假设这些后缀用于指示发向或发自Web服务的消息,并且会在生成的WSDL中创建相应的<wsdl:operation>元素,见PPT6。最后一个DynamicWsdlllDefinition的属性是  locationUri.这个属性告诉客户端服务可以在哪里发现。PPT8中的框图分解了locationUri属性中的URL。 谈论到<servlet-mapping>,我们需要在Web.xml中添加一个新的<servlet-mapping>,以便 MessageDispatcherServlet可以提供WSDL定义。
              <servlet-mapping>
                    <servlet-name>poker</servlet-name>
                    <url-pattern>*.wsdl</url-pattern>
        </servlet-mapping> 

      • 使用预定义的WSDL:DynamicWsdlllDefinition可以适用于大多数情况,但是,你也可能会遇到一些情况,必须对Web服务的WSDL定义进行更多的控制。在这种情况下,需要自己创建WSDL,接着使用SimpleWsdlllDefinition将它置入到Spring中: <bean id="poker" class="org.springframework.ws.wsdl.wsdlll.SimpleWsdlllDefinition">       <property name="wsdl" value="PokerService.wsdl"> </bean> 预定义WSDL的唯一一个问题(除了创建它很困难外)是它是被静态定义的。要想解决这个问题,可以让MessageDispatcherServlet为你重写WSDL。我们需要做的就是将一个名称为transformWsdlLocations的<init-param>设置成true,这样MessageDispatcherServlet就会从下面的位置得到它:<servlet>        <servlet-name>spring-ws</servlet-name>           <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>     <init-param>       <param-name>transformWsdlLocations</param-name>       <param-value>true</param-value>   </init-param></servlet>    

      部署服务:现在可以打包这个Web服务应用程序并进行部署了。因为这个项目选择了使用Maven 2,所以只需要简单的输入下面的命令就可以创建可部署的WAR文件: %   mvn   package   deploy  一旦Maven执行完毕,在目标目录中就会生成一个Poker-WS.war文件,它适合于部署到大多数Web应用服务器上。

    消费Spring-WS Web服务:使用Spring-WS建立Web服务仅仅展示了它的一半功能。Spring-WS还带有一个基于相同消息规范的客户端API。

    • 使用Web服务模板:WebServiceTemplate是Spring-WS客户端API的核心类,见PPT9。向Web服务发送消息需要生成对每个Web服务客户端都相同的SOAP封装和通信样本代码。在Spring中配置WebServiceTemplate相当简单:

      <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">

         <property name="messageFactory">

                 <bean class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">

         </property>

         <property name="messageSender" ref="messageSender" />

      </bean>

      WebServiceTemplate需要知道如何构建发送给服务的消息以及如何发送消息。被置入到messageFactory属性中的对象负责处理构建消息的任务。这个被置入的对象应该实现Spring-WS的WebServiceMessageFactory接口。幸运的是,不用自己去实现。Spring提供了3中选择,见PPT10。   messageSender属性应该被置入一个对WebServiceMessageSender实现的引用。Spring-WS提供了几种实现,Spring-WS提供了几种实现,见PPT11.  HttpUrlConnectionMessageSender在Spring中的配置如下:

      <bean id="messageSender" class="org.springframework.ws.transport.http.HttpUrlConnectionMessageSender">

              <property name="uri" value="http://localhost:8080/Poker-WS/services" />

      </bean>  

      url属性指定了服务的位置。注意,这个URL必须与服务WSDL定义中的URL匹配。

      • 发送消息:在WebServiceTemplate配置完毕后,可以向扑克评分服务发送和接收XML消息。WebServiceTemplate提供了几个用于发送和接收消息的方法。下面是一个最基本和最容易理解的方法:

        public boolean sendAndReceive(Source requestPayload, Result  responseResult  )throws Exception 。Source对象是发送给Web服务的消息载荷,Result对象则是由从服务返回的消息载荷装配而成的。程序清单见spring in action(P253)展示的TemplateBasedPokerClient是一个PokerClient接口的实现,它使用了WebServiceTemplate的sendAndReceive()方法与扑克评分服务进行通信。注意,WebServiceTemplate是通过装定器注入的。因此,TemplateBasedPokerClient必须按下面的方法在Spring中配置:

        <bean id="templateBasedClient" class=".....">

           <property name="webServiceTemplate" ref="webServiceTemplate" />

        </bean>  如果消息比较简单,手工创建和解析XML消息没有什么问题。但如果需要构建复杂的消息载荷,会需要大量的代码。幸运的是,你不必自己处理这些XML。前面你看到了端点是如何使用序列化器在对象和XML直接进行转换的。现在,我将向你展示WebServiceTemplate如何利用序列化器在客户端消除对XML处理代码的需求。

        在客户端使用序列化器:除了程序清单中的sendAndRecevie()方法,WebServiceTemplate还提供了marshalSendAndReceive()方法。通过这个方法发送和接收的XML消息可以被序列化/反序列化成Java对象。 使用marshalSendAndReceive()方法十分简单,只需要传入请求对象参数,并且将响应对象作为返回值。这扑克评分服务中,这些对象分别是EvaluateHandRequest和EvaluateHandResponse。见程序清单9.6 (P255)。 WebServiceTemplate并不知道任何有关序列化/反序列化这些对象的内容,不过它可以被置入一个序列化器和一个反序列化器。

        <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">

           <property name="messageFactory">

                   <bean class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">

           </property>

           <property name="messageSender" ref="messageSender" />

           <property name="marshaller" ref="marshaller"  />

           <property name="unmarshaller" ref="marshaller" />

        </bean>

        MarshallingPokerClient 比TemplateBasedPokerClient 更加简洁。但是,我们还需要进行一些工作才能使其更加简洁。来看看如何使用Spring-WS的WebServiceGatewaySupport类来取消对WebServiceTemplate的明确注入。见PPT12

      使用web服务的网关支持: Spring的数据访问API包含了一个很方便的支持类--这个类可以提供不需要被配置的模板。于此类似,Spring-WS也提供了一个支持类WebServiceGateWaySupport,可以为继承了它的客户端类自动提供WebServiceTemplate。 即使WebServiceTemplate不在需要在Spring中定义,你仍然需要通过WebServiceGatewaySupport的属性配置如果创建WebServiceTemplate的细节。

      <bean id="pokerClientGateway" class=".......">

         <property name="messageFactory">

                 <bean class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory">

         </property>

         <property name="messageSender" ref="messageSender" />

         <property name="marshaller" ref="marshaller"  />

         <property name="unmarshaller" ref="marshaller" />

      </bean>

      这些属性必须被配置为是针对WebServiceTemplate的。



  •                                                                                                        --    学海无涯