`

spring-ws框架开发webservice服务

 
阅读更多

目录

  • 开发环境
  • 利用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;
    }

}
 

 

这里求简,在访问这个方法的时候,默认只反馈一个固定内容的字符串数组。

 

这里需要注意几点:

  1. 这个endpoint类的包路径(后面会用到)是 cn.com.webxml.webservice.endpoint
  2. 类头部上的@Endpoint标注
  3. 方法声明上的 @PayloadRoot 标注中的namespace和localPart分别就是wsdl中的targetNamespace和soap方法名称
  4. @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/

  • 大小: 164.2 KB
  • 大小: 167.7 KB
分享到:
评论

相关推荐

    spring-ws开发/调用webservice示例代码

    Spring-WS是一个基于Java的轻量级Web服务框架,它专注于提供SOAP协议的支持,用于创建和消费Web服务。本示例代码将展示如何利用...通过学习和实践,我们可以深入理解Web服务的工作原理,并掌握Spring-WS框架的使用。

    使用 Spring-WS 完成的 Web Service (SOAP)

    综上所述,使用 Spring-WS 开发 Web Service 需要对 SOAP 协议、XML Schema、WSDL 以及 Spring 框架有深入理解。通过源码分析和工具应用,我们可以更有效地构建、测试和部署高质量的 Web Service 应用。在实际项目中...

    Spring-ws搭建WebService服务端demo

    在本文中,我们将深入探讨如何使用Spring框架与Spring-WS模块构建一个Web Service服务端的示例。Spring-WS是一个基于XML的Web Service框架,它提供了创建SOAP Web Service的简单方式。结合Maven进行项目管理,我们...

    Spring-WS示例

    Spring-WS 是一个基于 Java 的轻量级 Web 服务框架,它专注于 SOAP(Simple Object Access Protocol)消息处理。这个框架使开发者能够创建和消费符合 W3C 标准的 SOAP 消息,同时利用 Spring 框架的强大功能,如依赖...

    JAX-WS + Spring 实现webService示例

    本示例将详细阐述如何利用Java API for XML Web Services (JAX-WS) 和Spring框架来创建和消费Web Service。 **JAX-WS简介** JAX-WS是Java平台上用于构建和部署SOAP(Simple Object Access Protocol)Web服务的标准...

    Spring Integration + Spring WS 整合

    在 Java 领域中,Spring Integration 和 Spring WS 是两个常用的框架,它们分别负责集成系统和 Web 服务。今天,我们将探讨如何将这两个框架整合在一起,实现一个完整的 Web 服务解决方案。 Spring Integration ...

    maven-spring-mybatis-webservice整合

    Spring框架也提供了对WebService的支持,可以方便地在Spring应用中集成和消费Web服务。 "Maven-spring-mybatis-web"的整合过程中,通常会包含以下步骤: 1. 创建Maven项目,并在pom.xml中配置好依赖。 2. 配置...

    spring-boot-webservice例子

    Spring Boot以其简化Java应用程序开发而闻名,而Apache CXF则是一个强大的Web服务框架,它支持JAX-WS(Java API for XML Web Services)标准。让我们深入探讨这两个技术的结合及其在创建Web服务中的应用。 首先,`...

    jax-ws与spring整合jar包下载

    在Java世界中,开发Web服务时常常会遇到两种技术:JAX-WS(Java API for XML Web Services)和Spring框架。JAX-WS是用于创建和消费SOAP Web服务的标准API,而Spring则是一个广泛使用的轻量级应用框架,提供依赖注入...

    Spring应用开发实战Web Service WS

    Spring是目前非常流行的Java应用框架,其提供了丰富的开发组件,WS(Web Service)是其中重要的组成部分。Web Service是一种基于Web的分布式计算技术,它使得不同应用程序能够通过Internet通信和交互。Spring对WS的...

    jax-ws webservice简单demo

    在实际项目中,JAX-WS可以与其他技术,如Spring框架,结合使用,以实现更高级的功能,如事务管理、安全性等。 在"jax-ws webservice完整demo"中,你可以通过查看源码了解每个组件的具体实现,并学习如何将它们组合...

    maven-spring-mybatis-web+webservice

    在现代Web开发中,Maven、Spring、MyBatis和Web服务(Webservice)是四个关键的组件,它们共同构建了一个强大且灵活的后端架构。这个项目组合,简称为“mmsw”,是一个基于Java的技术栈,用于构建高效、可维护的Web...

    MyEclipse6_5上基于JAX-WS开发Webservice(中文示例)

    虽然标题未直接提及Spring,但在开发Web服务时,Spring框架常被用来管理服务的生命周期和依赖关系。Spring可以与JAX-WS集成,提供更高级的功能,如AOP(面向切面编程)和声明式事务管理。 5. **XFire**: XFire是...

    Spring Webservice Demo(含客户端)

    Spring WebService是一个基于Java的框架,用于构建和消费Web服务。这个"Spring Webservice Demo"包含了一个客户端,意味着它提供了一个完整的端到端示例,从服务端的创建到客户端的调用。让我们深入探讨一下这个示例...

    在spring中开发webservice

    通过本章节的学习,读者不仅可以了解到WebService的基本概念和发展背景,还能掌握如何使用Spring-WS这一强大的框架来构建高效、安全且易于维护的Web服务。这对于从事企业级应用开发的专业人士来说是非常宝贵的资源。

    Spring3_JAX-WS

    Spring 3.2.8是该框架的一个稳定版本,它包含了对Web服务的支持,尤其是JAX-WS的集成。 **JAX-WS** 是一种用于构建和部署Web服务的标准API,它使用SOAP(Simple Object Access Protocol)协议进行通信。通过JAX-WS...

    JAX-WS + Spring 实现webService示例.docx

    在本文中,我们将深入探讨如何使用JAX-WS(Java API for XML Web Services)与Spring框架集成来实现一个Web服务示例。这个示例将帮助我们理解如何在Spring环境中创建、配置和部署JAX-WS服务,同时利用Spring的依赖...

    spring+xfire( 编写webservice完整配置+案例)

    Spring 和 XFire 是一个经典的组合,用于在Java应用程序中...虽然现在Spring社区更倾向于使用Spring-WS或其他现代的Web服务框架,如Apache CXF,但理解这个历史背景和工作原理对于理解Web服务的发展历程仍然非常重要。

Global site tag (gtag.js) - Google Analytics