目录
- 开发环境
- 利用jaxb2的maven插件根据WSDL生成对应的POJO
- 开发和配置endpoint
- 配置web.xml
- 启动servlet容器
- 验证webservice服务的可用性
- 检查wsdl
- 访问webservice
- 有关spring-ws实现和其他使用的问题
- 参考资料
开发环境
| eclipse ide | 4.3.2 |
| spring | 4.0.6 |
| spring-ws-core | 2.1.3 |
开发环境说明:之前没了解过,以为只有spring4.x才有spring-ws的框架支持,后来看一下spring 3.x应该也是有对应版本的spring-ws,所以不一定版本需要用到这么新,也可以根据自己的情况酌情选择其他版本。
利用jaxb2的maven插件根据WSDL生成对应的POJO
创建项目目录:
mkdir -p webservice_sample/src/{main,test}/{java,resources}; mkdir -p webservice_sample/src/main/webapp
目录结构如下:
webservice_sample$ tree
.
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ ├── resources
│ │ └── webapp
│ └── test
│ ├── java
│ └── resources
└── target
├── classes
├── mvn-eclipse-cache.properties
└── test-classes
在src/main/resources/创建目录 wsdl
在目录 src/main/resources/wsdl 当中下载WSDL
wget http://webservice.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?WSDL
进入wsdl目录后,将文件名中的问号改成句点,重命名为 IpAddressSearchWebService.asmx.WSDL
进入目录webservice_sample 编写pom.xml,如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>caesar.com</groupId>
<artifactId>webservice_sample</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>mock Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<java_source_version>1.6</java_source_version>
<java_target_version>1.6</java_target_version>
<maven_compiler_plugin_version>2.5.1</maven_compiler_plugin_version>
<maven_jaxb2_version>0.9.0</maven_jaxb2_version>
<maven_jaxb2_forceRegenerate>false</maven_jaxb2_forceRegenerate>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-xml</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-support</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-oxm</artifactId>
<version>1.5.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-all</artifactId>
<version>7.2.0.v20101020</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.7</version>
</dependency>
<dependency>
<groupId>apache-log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java/</directory>
</resource>
<resource>
<directory>src/main/resources/</directory>
</resource>
<resource>
<directory>src/main/webapp/</directory>
</resource>
</resources>
<finalName>mock</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.2.0.v20101020</version>
<configuration>
<!-- specify jetty port -->
<jettyConfig>${basedir}/src/main/resources/jetty.xml</jettyConfig>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven_compiler_plugin_version}</version>
<configuration>
<source>${java_source_version}</source>
<target>${java_target_version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- disable genereate java code from wsdl begin -->
<!-- wsdl to java code for separate av provider -->
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>${maven_jaxb2_version}</version>
<executions>
<execution>
<id>alibaba_av</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<schemaLanguage>WSDL</schemaLanguage>
<generateDirectory>${basedir}/src/main/java/</generateDirectory>
<generatePackage>cn.com.webxml.webservice.wsdl.ipaddresssearch</generatePackage>
<forceRegenerate>${maven_jaxb2_forceRegenerate}</forceRegenerate>
<encoding>UTF-8</encoding>
<schemas>
<schema>
<!-- <url>http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL</url> -->
<url>file:${basedir}/src/main/resources/wsdl/IpAddressSearchWebService.asmx.WSDL</url>
</schema>
</schemas>
</configuration>
</execution>
</executions>
</plugin>
<!-- disable genereate java code from wsdl end -->
</plugins>
</build>
</project>
在 webservice_sample 目录中执行如下指令,创建eclipse工程
mvn eclipse:clean eclipse:eclipse -DdownloadSources=true
导入eclipse中,会看到如下截图:

包路径 cn.com.webxml.webservice.wsdl.ipaddresssearch 中的代码就是我们在pom当中如下这段声明生成的(具体配置方法可以参考插件所在的网站文档):
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
……
</plugin>
可能有细心的朋友会发现生成WSDL文档对应的pojo代码的声明内容当中注释了一段
<!-- <url>http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx?WSDL</url> -->本来是打算用这个webservice来做示例,但是因为执行mvn eclipse:eclipse的时候发生了错误,大家可以自己试验一下看看问题在哪里(我暂时没有去排查这个原因,所以先存疑。
)
开发和配置endpoint
前面只是准备好了wsdl和与xml的对应转换pojo而已,现在要看看如何开发endpoint
打开IpAddressSearchWebService.asmx.WSDL,如下图所示:

准备开发一个soap的方法 getCountryCityByIp,我们可以编写如下endpoint:
package cn.com.webxml.webservice.endpoint;
import java.util.Arrays;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.SoapMessage;
import cn.com.webxml.webservice.wsdl.ipaddresssearch.ArrayOfString;
import cn.com.webxml.webservice.wsdl.ipaddresssearch.GetCountryCityByIp;
import cn.com.webxml.webservice.wsdl.ipaddresssearch.GetCountryCityByIpResponse;
@Endpoint
public class IpAddrSearchEndpoint {
private static final String NAMESPACE_URI = "http://WebXml.com.cn/";
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryCityByIp")
@ResponsePayload
public GetCountryCityByIpResponse getCountryCityByIp(@RequestPayload GetCountryCityByIp request,
SoapHeader soapHeader, SoapMessage soapMessage) {
// output(soapMessage);
GetCountryCityByIpResponse response = new GetCountryCityByIpResponse();
ArrayOfString value = new ArrayOfString() {
{
this.string = Arrays.asList("hongdulasi", "nibo'er");
}
};
response.setGetCountryCityByIpResult(value);
return response;
}
}
这里求简,在访问这个方法的时候,默认只反馈一个固定内容的字符串数组。
这里需要注意几点:
- 这个endpoint类的包路径(后面会用到)是 cn.com.webxml.webservice.endpoint
- 类头部上的@Endpoint标注
- 方法声明上的 @PayloadRoot 标注中的namespace和localPart分别就是wsdl中的targetNamespace和soap方法名称
- @ResponsePayload 和 @RequestPayload 这两个标注的用法,以及它们对应的数据类型就是此前通过maven插件对wsdl定义生成的java类
配置web.xml
虽然有了endpoint,我们依旧无法奔跑(run起来)我们的webservice的服务端,嗯,需要有一个servlet容器以及web.xml配置来衔接我们的spring容器以及endpoint类到运行时状态。
准备一个 web-ipaddresssearch.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>ipaddrsearch-spring-ws</servlet-name> <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ipaddrsearch-spring-ws</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
还有一个spring的配置文件 ipaddrsearch-spring-ws-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:sws="http://www.springframework.org/schema/web-services" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="cn.com.webxml.webservice.endpoint" /> <!-- 这里是让spring容器扫描这个包路径下的标注,这里就用到上面的endpoint所在的包路径了,当然可以指定更高一级的路径,扩大扫描的范围 --> <sws:annotation-driven /> <sws:static-wsdl id="IpAddressSearchWebService" location="classpath:wsdl/IpAddressSearchWebService.asmx.WSDL"/> <!-- 这里是用来指定静态wsdl定义的配置 --> </beans>
需要注意的内容:
web-ipaddresssearch.xml 和 ipaddrsearch-spring-ws-servlet.xml 之间是有对应关系的。
web-ipaddresssearch.xml 中的 “servlet-name”的内容就是 ipaddrsearch-spring-ws-servlet.xml 的前半部分。
配置endpoint的文件名称的命名规范可以看成是: <servlet-name>-servlet.xml 【其中<servlet-name>需要用你在web.xml当中配置的servlet-name的名称去替换】
启动servlet容器
好了都准备好了,该上servlet容器了,我才用了手写代码的笨办法,主要是少点配置,多点代码好调整一些。
package mock.webservice.server.main;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.webapp.WebAppContext;
public class RunJetty {
private static final String JETTY_CONNECTOR_NAME = "webservice_connector";
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Server server = new Server();
String currentPath = RunJetty.class.getResource("/").getPath();
System.out.println("currentPath = " + currentPath);
HandlerList handlerList = new HandlerList();
SelectChannelConnector connector_8080 = new SelectChannelConnector();
connector_8080.setPort(8080); // 端口号
connector_8080.setMaxIdleTime(30000);
connector_8080.setRequestHeaderSize(8192);
connector_8080.setName(JETTY_CONNECTOR_NAME);
server.addConnector(connector_8080);
WebAppContext customerWebAppContext = new WebAppContext();
customerWebAppContext.setDescriptor(String.format("%s/WEB-INF/web-ipaddresssearch.xml", currentPath));
customerWebAppContext.setResourceBase(currentPath);
customerWebAppContext.setContextPath("/ipaddress"); // context path
customerWebAppContext.setConnectorNames(new String[] { JETTY_CONNECTOR_NAME });
handlerList.addHandler(customerWebAppContext);
server.setHandler(handlerList);
server.start();
server.join();
}
}
直接启动RunJetty类,就能访问我们暴露的webservice的服务了。
验证webservice服务的可用性
检查wsdl
可以先通过
curl http://localhost:8080/ipaddress/IpAddressSearchWebService.wsdl
来查看wsdl定义。(注意:spring-ws框架的wsdl的访问路径的固定后缀就是wsdl,而其名称就是前面<sws:static-wsdl/> 中定义的id值)
访问webservice
首先访问官方的测试文档,打开URL: http://webservice.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?op=getCountryCityByIp
拷贝soap1.1中的内容,并稍作调整:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<getCountryCityByIp xmlns="http://WebXml.com.cn/">
<theIpAddress>test test</theIpAddress>
</getCountryCityByIp>
</soap:Body>
</soap:Envelope>
将这段内容保存在 /tmp/ipaddrsearch.xml 中,而后在命令行下使用curl访问webservice
curl -H "Content-Type:text/xml;charset=utf-8" -d @/tmp/ipaddrsearch.xml http://localhost:8080/ipaddress/IpAddressSearchWebService.asmx > /tmp/xml.tmp; xmllint --format /tmp/xml.tmp
可以得到反馈信息:
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:getCountryCityByIpResponse xmlns:ns2="http://WebXml.com.cn/">
<ns2:getCountryCityByIpResult>
<ns2:string>hongdulasi</ns2:string>
<ns2:string>nibo'er</ns2:string>
</ns2:getCountryCityByIpResult>
</ns2:getCountryCityByIpResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
我们期望的结果出现了。
需要注意的是:
其实我们的访问URL并没有特别的约束,其核心部分是:
curl -H "Content-Type:text/xml;charset=utf-8" -d @/tmp/ipaddrsearch.xml http://localhost:8080/ipaddress/IpAddressSearchWebService.asmx
这里的URL后面的“IpAddressSearchWebService.asmx”这段可以改成其他任何字符串都是ok的。
因为在 /tmp/ipaddrsearch.xml 当中的请求内容已经将webservice请求的namespace和soap方法说明的比较清楚了,spring-ws框架已经能够定位到我们所编写的endpoint类。
有关spring-ws实现和其他使用的问题
【实现】spring-ws是如何定位到endpoint类其中的方法的?
【使用】文中没有提到一个很常用的场景——soapheader进行权限验证应该如何实现?
参考资料
spring官方文档: http://docs.spring.io/spring-ws/docs/2.2.0.RELEASE/reference/htmlsingle/
如果需要观察webservice调用情况,可以通过tcpdump获取抓包的内容(比如文中端口是8080,网卡假定名称为eth1,操作系统为linux)写入一个固定为(比如下面指令的 /tmp/capture),则可以使用如下指令:
sudo tcpdump -i eth1 port 8080 -w /tmp/capture
这篇文档讲了如何将jdk的DomSource、String类型的xml文档输出成稍微有点缩进的样子(虽然不够pretty,但是也还好了)
http://stackoverflow.com/questions/139076/how-to-pretty-print-xml-from-java
自动拷贝依赖包(如果需要将文中的代码打包放到某台固定机器的话,会需要所有依赖包合并的到一起,方便启动)
http://www.ibm.com/developerworks/cn/java/j-5things13/

本文介绍如何使用 Spring WS 框架搭建 SOAP Web 服务,包括根据 WSDL 生成 POJO 类、开发 Endpoint 和配置 Servlet 容器等步骤。
5260

被折叠的 条评论
为什么被折叠?



