关于Spring Web Services框架
Spring web services(以下简称:Spring WS)框架是springframework东家SpringSource公司旗下的一个子项目。目前的版本是1.5M1,最新版本可以从spirngframework网站下载,项目地址是:http://www.springframework.org/node/567
由于怀着对spring框架的热爱,于是打算学习下这个框架。
Spring Web Services框架的特点
-
Spring框架的支持,你可以重用Spring的所有特性。
-
Spring WS使用Contract First(自顶向下)的设计方式。Spring WS不支持Contract Last(自底向上)
-
支持几乎所有的XML API,处理传入XML消息的时候就不限于JAX-P,可以是任意的XML API,选择你所擅长的。
- 灵活的XML Marshalling,Object/XML mapping产品的支持,包括JAX-B1,2,XMLBean以及Castor等等
- Security支持,集成了Acegi.实现web services 认证。
Spring Web Services框架的分析
1.为什么使用Contract First.
最佳实践认为:使用自顶向下的设计方式也就是采用XML/XSD to JAVA可以获得更多的益处,包括以下几点.
- 重用性,web services或者SOA的很大优势在于对业务的快速响应,那么设计与开发web services的时候如果能够在服务的重用上做足,做强,无疑在以后业务的响应上会带来莫大的好处,使用Contract First,使用XML/XSD定义服务,你可以获得重用,而java则很难做到这一点。
- 性能,web servers的性能一直是众多人士关注的,采用Contract Last经常会由于java的引用造成内存中存在众多的reference,假设一个java 对象引用了5个以上的其他对上,再把这些对象转换成XML,可想而知。必然加大内存的开销,(XML里面表述起来就像有5个字节点一样,那么更多呢?)采用Contract First,你很明白的所想要的服务,你通过撰写XSD来描述你的服务,你很清楚你的引用。
- 版本,使用Contract Last的时候,快速响应的服务是会经常修改来适应新的业务要求,你发布服务通过java类来开始,那么新的服务在java中意味着新的接口以及新的实现,那么怎么办?废弃原来的?也许原来的还在使用,那么你得保持两个服务。采用Contract Last,由于Contract的松散耦合,它允许你存在两套服务的同时,并且只有一个实现。
这样造成了Contract Last的问题:自底向上生成经常会得到无法重用的类型定义以及多个定义为表示语义等效信息的类型。相比而言: XML 模式规范定义范围比 Java 更广的用于描述消息结构的构造。其中包括各个选择、限制的派生、Annotation 及其他。因此,与采用其他方式相比,使用 WSDL 和 XSD 定义接口并生成框架 Java 代码的方式更好
比较二者,其实最大优劣的莫过于服务的变化性,Contract Last会让服务难于修改和快速变更,难于重用,用java开始设计,那么你最好保证你的服务是永久不改变的,或者事先你得反复审查你的服务接口。使其尽量保持不变性。
2.例子引入
Spring Web Servers提供了丰富的例子可供学习,下载其完整包可以在samples下面找到。这里也引用其中一个Echo sample介绍其开发过程。该例子实现web services client调用服务传送名字到服务器。然后服务提供者接收该名字,并附加信息返回给调用者。
定义你的XML
- <sch:EchoRequest xmlns:sch="http://www.upyaya.org/echo/schemas">
- <sch:Echo>
- <sch:Name>string</sch:Name>
- </sch:Echo>
- </sch:EchoRequest>
<sch:EchoRequest xmlns:sch="http://www.upyaya.org/echo/schemas">
<sch:Echo>
<sch:Name>string</sch:Name>
</sch:Echo>
</sch:EchoRequest>
使用XML Buddy转换成XSD.
- <?xml version="1.0" encoding="UTF-8"?>
-
- <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
- targetNamespace="http://www.upyaya.org/echo/schemas"
- xmlns="http://www.upyaya.org/echo/schemas"
- elementFormDefault="qualified">
- <xs:element name="Echo">
- <xs:complexType>
- <xs:sequence>
- <xs:element ref="Name"/>
- </xs:sequence>
- </xs:complexType>
- </xs:element>
- <xs:element name="EchoRequest">
- <xs:complexType>
- <xs:sequence>
- <xs:element ref="Echo"/>
- </xs:sequence>
- </xs:complexType>
- </xs:element>
- <xs:element name="Name" type="xs:string"/>
- </xs:schema>
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated from echo.xml by XMLBuddy -->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.upyaya.org/echo/schemas"
xmlns="http://www.upyaya.org/echo/schemas"
elementFormDefault="qualified">
<xs:element name="Echo">
<xs:complexType>
<xs:sequence>
<xs:element ref="Name"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="EchoRequest">
<xs:complexType>
<xs:sequence>
<xs:element ref="Echo"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Name" type="xs:string"/>
</xs:schema>
当你熟悉了XSD的写法的时候,完全不用前面的XML开路。编辑一下XSD让其好看点,并未这个定义加上web services响应代码。结果如下:
- <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
- xmlns:tns="http://www.upyaya.org/echo/schemas" elementFormDefault="qualified"
- targetNamespace="http://www.upyaya.org/echo/schemas">
- <xs:element name="EchoRequest">
- <xs:complexType>
- <xs:all>
- <xs:element name="Echo" type="tns:EchoType"/>
- </xs:all>
- </xs:complexType>
- </xs:element>
- <xs:element name="EchoResponse">
- <xs:complexType>
- <xs:all>
- <xs:element name="EchoResponse" type="tns:ReturnType"/>
- </xs:all>
- </xs:complexType>
- </xs:element>
- <xs:complexType name="EchoType">
- <xs:sequence>
- <xs:element name="Name" type="xs:string"/>
- </xs:sequence>
- </xs:complexType>
-
- <xs:complexType name="ReturnType">
- <xs:sequence>
- <xs:element name="Message" type="xs:string"/>
- </xs:sequence>
- </xs:complexType>
- </xs:schema>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.upyaya.org/echo/schemas" elementFormDefault="qualified"
targetNamespace="http://www.upyaya.org/echo/schemas">
<xs:element name="EchoRequest">
<xs:complexType>
<xs:all>
<xs:element name="Echo" type="tns:EchoType"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:element name="EchoResponse">
<xs:complexType>
<xs:all>
<xs:element name="EchoResponse" type="tns:ReturnType"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:complexType name="EchoType">
<xs:sequence>
<xs:element name="Name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ReturnType">
<xs:sequence>
<xs:element name="Message" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
至此你的服务就完成。EchoRequest用于发送请求,EchoResponse用于响应。
定义后台服务接口。代码
- package org.upyaya.sample.echo.service;
-
- public interface EchoService {
- public String echo(String name);
- }
package org.upyaya.sample.echo.service;
public interface EchoService {
public String echo(String name);
}
实现如下:为了简单,略去后台的调用。
- package org.upyaya.sample.echo.service;
-
- import java.text.SimpleDateFormat;
- import java.util.Calendar;
-
- import org.upyaya.sample.echo.domain.dao.EchoDao;
-
- public class EchoServiceImpl implements EchoService {
-
-
- public String echo(String name) {
- if (name == null || name.trim().length() == 0) {
- return "echo back: -please provide a name-";
- }
- SimpleDateFormat dtfmt = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss a");
- return "echo back: name " + name + " received on "
- + dtfmt.format(Calendar.getInstance().getTime());
- }
-
-
-
-
-
-
-
-
- }
package org.upyaya.sample.echo.service;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.upyaya.sample.echo.domain.dao.EchoDao;
public class EchoServiceImpl implements EchoService {
//private EchoDao echoDao;
public String echo(String name) {
if (name == null || name.trim().length() == 0) {
return "echo back: -please provide a name-";
}
SimpleDateFormat dtfmt = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss a");
return "echo back: name " + name + " received on "
+ dtfmt.format(Calendar.getInstance().getTime());
}
/* public EchoDao getEchoDao() {
return echoDao;
}
public void setEchoDao(EchoDao echoDao) {
this.echoDao = echoDao;
}*/
}
Spring的ApplicationContext就是Spring的魔法棒,指挥着组件间的合作。
1) 几个类的介绍:
-
org.springframework.ws.transport.http.MessageDispatcherServlet,类比Spring MVC的DispacherServlet,它把其中的三个关键类WebServiceMessageReceiverHandlerAdapter,MessageDispatcher,WsdlDefinitionHandlerAdapter,分开来处理。该类放置在web.xml里面。这些类的功能可能的话在下篇的源码分析中介绍。
-
org.springframework.ws.soap.server.SoapMessageDispatcher,类比于Spring MVC的DispatherServlet.这里dispather的是传入的web services 消息到endpoint. 此类是WebServiceMessageReceiver的子类,MessageDispatcherServlet会在加载的时候去初始化它。他的功能是注册EndpointMapping,并使传入的消息mapping到特定的endpoint对象 此类通常定义在spring的application context里面。
-
Endpoint--MessageEndpoint,MethodEndpoint,PayloadEndpoint,类比于Spring MVC的Controller.功能是把传入的消息传递到服务器。然后转换成响应(如果有响应)。此类就像Spring MVC里面一样,需要自己实现。
-
EndpointMapping,类似于Spirng MVC的controller mapping,把消息mapping到特定的处理类(Endpoint的子类或者实现类)。常用的两个是:PayloadRootQNameEndpointMapping,SoapActionEndpointMapping
-
org.springframework.ws.wsdl.wsdl11.DynamicWsdl11Definition,此类为动态发布wsdl的定义类。
2)Endpoint的实现,
endpoint是把传入消息处理后转为响应的类,通过继承AbstractMarshallingPayloadEndpoint重写invokeInternal方法来实现,invokeInternal
是这样的:protected Object invokeInternal(Object request) throws Exception,传入的是请求消息。返回的是响应消息。代码如下
- package org.upyaya.sample.echo.endpoint;
-
- import org.springframework.ws.server.endpoint.AbstractMarshallingPayloadEndpoint;
- import org.upyaya.sample.echo.domain.schema.EchoRequest;
- import org.upyaya.sample.echo.domain.schema.EchoResponse;
- import org.upyaya.sample.echo.domain.schema.EchoType;
- import org.upyaya.sample.echo.domain.schema.ReturnType;
- import org.upyaya.sample.echo.service.EchoService;
-
- public class EchoMasharlingEndpoint extends AbstractMarshallingPayloadEndpoint {
- private EchoService echoService;
-
- public EchoService getEchoService() {
- return echoService;
- }
-
- public void setEchoService(EchoService echoService) {
- this.echoService = echoService;
- }
-
- protected Object invokeInternal(Object request) throws Exception {
- EchoRequest echoRequest = (EchoRequest)request;
- EchoType echoType = echoRequest.getEcho();
- System.out.println("-------here----------");
- System.out.println(echoType.getName());
- String returnMessage = echoService.echo(echoType.getName());
-
- EchoResponse response = new EchoResponse();
- ReturnType returnType = new ReturnType();
- returnType.setMessage(returnMessage);
- response.setEchoResponse(returnType);
- return response;
- }
- }
package org.upyaya.sample.echo.endpoint;
import org.springframework.ws.server.endpoint.AbstractMarshallingPayloadEndpoint;
import org.upyaya.sample.echo.domain.schema.EchoRequest;
import org.upyaya.sample.echo.domain.schema.EchoResponse;
import org.upyaya.sample.echo.domain.schema.EchoType;
import org.upyaya.sample.echo.domain.schema.ReturnType;
import org.upyaya.sample.echo.service.EchoService;
public class EchoMasharlingEndpoint extends AbstractMarshallingPayloadEndpoint {
private EchoService echoService;
public EchoService getEchoService() {
return echoService;
}
public void setEchoService(EchoService echoService) {
this.echoService = echoService;
}
protected Object invokeInternal(Object request) throws Exception {
EchoRequest echoRequest = (EchoRequest)request;
EchoType echoType = echoRequest.getEcho();
System.out.println("-------here----------");
System.out.println(echoType.getName());
String returnMessage = echoService.echo(echoType.getName());
EchoResponse response = new EchoResponse();
ReturnType returnType = new ReturnType();
returnType.setMessage(returnMessage);
response.setEchoResponse(returnType);
return response;
}
}
注:本例采用的是Marshalling方式。因此需要使用JAX-B的API来对消息进行转换,JAX-B的eclipse插件可以轻松的实现XSD->JAVA.插件地址:https://jaxb-workshop.dev.java.net/
3.总结和附件
Spring Web services的优缺点。
1)优点方面
-
Spring这个流行框架的支持。
-
继承了Spring框架的一贯优点,采用依赖注入方式。
-
集成较多的处理工具。XML API. 消息处理比较完善。
-
安全性的支持比较好。主要支持Acegi,也支持SimpleTextPassword。不过目前的SAAJ框架存在bug,简单认证还有一定的问题。
-
良好的API文档说明和例子学习。
2)缺点方面
-
客户端的支持比较简单,需要加强。
-
由于以前没咋用其他框架,缺点方面需要大家指出。
暂时做个总结吧