杰瑞科技汇

java 调 webservice

方法概览

方法 优点 缺点 适用场景
JDK 自带的 javax.xml.ws 无需额外依赖,简单直接 仅支持 SOAP 1.1/1.2,功能有限,对复杂类型支持较差 快速调用简单的、基于 SOAP 的 WebService,或学习原理。
Apache CXF 功能强大,灵活,支持 SOAP 1.1/1.2,易于集成到 Spring 需要引入外部依赖,配置稍复杂 生产环境首选,功能全面的 WebService 客户端和服务端框架。
Spring Boot + WebService Template 与 Spring 生态无缝集成,配置简单,代码优雅 需要 Spring Boot 环境 已经在使用 Spring/Spring Boot 的项目中,非常推荐。
动态调用 (不生成客户端) 无需生成客户端代码,灵活性高 代码可读性差,调试困难,容易出错 调用多个不同 WSDL 但结构相似的 WebService,或 WSDL 地址不固定时。

准备工作:获取 WebService 的信息

在编写代码之前,你需要从提供方获取以下关键信息,通常这些信息可以在 WSDL (Web Services Description Language) 文件中找到:

java 调 webservice-图1
(图片来源网络,侵删)
  1. WSDL 地址:服务的描述文件,包含了服务的所有信息(端口、方法、参数、返回值类型等)。
  2. 目标命名空间 (Target Namespace):一个唯一的 URI,用于标识这个服务。
  3. 服务端点地址:实际调用服务的 URL。
  4. 服务名:WSDL 中 <service> 标签的 name 属性。
  5. 端口名:WSDL 中 <port> 标签的 name 属性。
  6. 操作名:你想要调用的方法名。

示例 WSDL 信息 (我们将在示例中使用):

  • WSDL 地址: http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl
  • 目标命名空间: http://WebXml.com.cn/
  • 服务名: MobileCodeWS
  • 端口名: MobileCodeWSSoap
  • 操作名: getMobileCodeInfo
  • 方法签名: getMobileCodeInfo(String mobileCode, String userID)

使用 JDK 自带的 javax.xml.ws (JAX-WS)

这是最基础的方法,不需要引入任何第三方库,JDK 内置了 wsimport 工具来根据 WSDL 生成客户端代码。

步骤 1: 生成客户端代码

打开你的命令行工具,执行以下命令:

# -keep: 生成源代码
# -d: 指定编译后的 .class 文件存放目录
# -p: 指定生成的包名
wsimport -keep -d . -p com.example.webservice.client http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl

执行后,会在你指定的目录下生成一堆 .java.class 文件。

java 调 webservice-图2
(图片来源网络,侵删)

步骤 2: 编写调用代码

import com.example.webservice.client.MobileCodeWS;
import com.example.webservice.client.MobileCodeWSLocator;
import com.example.webservice.client.MobileCodeWSSoap;
public class JdkWebserviceClient {
    public static void main(String[] args) {
        try {
            // 1. 创建服务实例 (通过 Locator)
            MobileCodeWS service = new MobileCodeWSLocator();
            // 2. 获取 Port (服务端点接口)
            MobileCodeWSSoap port = service.getMobileCodeWSSoap();
            // 3. 直接调用方法
            String result = port.getMobileCodeInfo("13800138000", "");
            // 4. 处理结果
            System.out.println("调用结果: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

优点:非常简单,适合快速验证。 缺点wsimport 过程略显繁琐,且对复杂 XML 结构(如 List、自定义对象)的支持不够友好。


使用 Apache CXF (推荐)

CXF 是一个功能强大的开源框架,是目前生产环境中使用最广泛的方案之一,它支持从 WSDL 自动生成客户端,也支持手动创建客户端。

步骤 1: 添加 Maven 依赖

pom.xml 中添加 CXF 的依赖:

<dependencies>
    <!-- CXF 核心依赖 -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-frontend-jaxws</artifactId>
        <version>3.4.5</version> <!-- 使用最新稳定版本 -->
    </dependency>
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-rt-transports-http</artifactId>
        <version>3.4.5</version>
    </dependency>
    <!-- 如果需要日志来查看请求/响应,可以添加 slf4j 和 logback -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.36</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.11</version>
    </dependency>
</dependencies>

步骤 2: 使用 CXF 自动生成客户端 (推荐)

CXF 提供了 cxf-codegen-plugin Maven 插件,可以在编译时自动生成客户端代码,比手动执行 wsimport 更方便。

java 调 webservice-图3
(图片来源网络,侵删)

pom.xml<build> 标签内添加:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-codegen-plugin</artifactId>
            <version>3.4.5</version>
            <executions>
                <execution>
                    <id>generate-sources</id>
                    <phase>generate-sources</phase>
                    <configuration>
                        <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
                        <wsdlOptions>
                            <wsdlOption>
                                <wsdl>http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl</wsdl>
                            </wsdlOption>
                        </wsdlOptions>
                    </configuration>
                    <goals>
                        <goal>wsdltojava</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

然后执行 mvn clean compile,CXF 会自动生成代码到 target/generated-sources/cxf 目录。

步骤 3: 编写调用代码

生成的代码与 JDK 方式类似,但 CXF 的客户端创建方式略有不同。

import com.example.webservice.client.MobileCodeWS;
import com.example.webservice.client.MobileCodeWSImplService;
import com.example.webservice.client.MobileCodeWSSoap;
public class CxfWebserviceClient {
    public static void main(String[] args) {
        try {
            // 1. 创建服务实现类实例
            MobileCodeWSImplService service = new MobileCodeWSImplService();
            // 2. 获取 Port (CXF 推荐使用这种方式)
            MobileCodeWSSoap port = service.getMobileCodeWSSoap12(); // 注意:这里是 getMobileCodeWSSoap12
            // 3. 调用方法
            String result = port.getMobileCodeInfo("13800138000", "");
            // 4. 处理结果
            System.out.println("调用结果: " + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

优点:功能强大,支持多种协议和传输方式,社区活跃,是事实上的标准。 缺点:需要引入额外的依赖。


使用 Spring Boot + WebServiceTemplate

如果你的项目已经是 Spring Boot 应用,那么使用 WebServiceTemplate 是最优雅的方式。

步骤 1: 添加 Maven 依赖

除了 Spring Boot 的 web 依赖,你还需要 spring-ws-core

<dependencies>
    <!-- Spring Boot Web Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Web Services -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web-services</artifactId>
    </dependency>
</dependencies>

步骤 2: 配置 WebServiceTemplate

在 Spring Boot 的主配置类或一个专门的 @Configuration 类中配置 WebServiceTemplate

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.client.core.WebServiceTemplate;
@Configuration
public class WebserviceConfig {
    @Bean
    public Jaxb2Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        // 设置要生成的包名,这个包名必须与 WSDL 生成的客户端代码的包名一致
        marshaller.setPackagesToScan("com.example.webservice.client");
        return marshaller;
    }
    @Bean
    public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller) {
        WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
        webServiceTemplate.setMarshaller(marshaller);
        webServiceTemplate.setUnmarshaller(marshaller);
        // 设置默认的 URI,即 WebService 的地址
        webServiceTemplate.setDefaultUri("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx");
        return webServiceTemplate;
    }
}

步骤 3: 创建 Service 并调用

import com.example.webservice.client.GetMobileCodeInfo;
import com.example.webservice.client.GetMobileCodeInfoResponse;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.soap.client.core.SoapActionCallback;
import javax.xml.bind.JAXBElement;
public class MobileCodeService {
    private final WebServiceTemplate webServiceTemplate;
    public MobileCodeService(WebServiceTemplate webServiceTemplate) {
        this.webServiceTemplate = webServiceTemplate;
    }
    public String getMobileInfo(String mobileCode) {
        // 1. 创建请求对象 (需要是 WSDL 生成的类型)
        GetMobileCodeInfo request = new GetMobileCodeInfo();
        request.setMobileCode(mobileCode);
        request.setUserID("");
        // 2. 定义 SOAP Action (非常重要,通常在 WSDL 的 <binding> 中定义)
        String soapActionUri = "http://WebXml.com.cn/getMobileCodeInfo";
        // 3. 发送请求并获取响应
        JAXBElement<GetMobileCodeInfoResponse> response = (JAXBElement<GetMobileCodeInfoResponse>) webServiceTemplate
                .marshalSendAndReceive(request, new SoapActionCallback(soapActionUri));
        // 4. 从响应对象中提取结果
        return response.getValue().getGetMobileCodeInfoResult();
    }
}

步骤 4: 创建 Controller 进行测试

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/mobile")
public class MobileCodeController {
    @Autowired
    private MobileCodeService mobileCodeService;
    @GetMapping("/{code}")
    public String getMobileCodeInfo(@PathVariable String code) {
        return mobileCodeService.getMobileInfo(code);
    }
}

启动 Spring Boot 应用,访问 http://localhost:8080/api/mobile/13800138000 即可看到结果。

优点:与 Spring 生态完美集成,代码结构清晰,易于管理。 缺点:必须基于 Spring/Spring Boot 框架。


动态调用 (不生成客户端)

当你不想或不能生成客户端代码时,可以使用这种方式,它通过直接构造 SOAP 请求 XML 来调用服务。

步骤 1: 添加依赖 (可以使用 CXF 或 JDK 自带的)

<!-- 使用 CXF -->
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>3.4.5</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>3.4.5</version>
</dependency>

步骤 2: 编写调用代码

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.jaxws.JaxWsProxyFactoryClient;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
public class DynamicWebserviceClient {
    public static void main(String[] args) {
        // 1. 创建工厂
        JaxWsProxyFactoryClient factory = new JaxWsProxyFactoryClient();
        // 2. 设置服务地址
        factory.setAddress("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx");
        // 3. 设置服务接口 (需要你手动创建一个与 WSDL 中 PortType 对应的 Java 接口)
        // 这是一个关键步骤,你需要根据 WSDL 手动定义这个接口。
        // interface MobileCodeWSSoap {
        //     String getMobileCodeInfo(String mobileCode, String userID);
        // }
        factory.setServiceClass(MobileCodeWSSoap.class); // 假设你已手动创建此接口
        // 4. 创建客户端代理
        MobileCodeWSSoap soap = (MobileCodeWSSoap) factory.create();
        // 5. (可选) 设置超时等连接属性
        Client client = ClientProxy.getClient(soap);
        HTTPConduit conduit = (HTTPConduit) client.getConduit();
        HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
        httpClientPolicy.setConnectionTimeout(10000); // 连接超时 10 秒
        httpClientPolicy.setReceiveTimeout(20000);   // 接收超时 20 秒
        conduit.setClient(httpClientPolicy);
        // 6. 调用方法
        String result = soap.getMobileCodeInfo("13800138000", "");
        // 7. 处理结果
        System.out.println("动态调用结果: " + result);
    }
}

优点:最大的灵活性,无需生成任何代码。 缺点

  • 必须手动创建服务接口,这本身就相当于“部分”生成了代码。
  • 无法在编译时检查错误,如果方法名或参数写错,只有在运行时才会报错。
  • 调试困难,需要查看原始 XML。

总结与建议

场景 推荐方法
新手学习 / 快速验证 / 简单调用 JDK javax.xml.ws
全新 Java 项目 / 生产环境 Apache CXF
已在使用 Spring Boot 的项目 Spring Boot + WebServiceTemplate
需要调用多个不同 WSDL 服务 / WSDL 地址不固定 动态调用

对于绝大多数现代 Java 项目,Apache CXF 是最稳妥、功能最全面的选择,如果你的项目已经是 Spring 生态的一部分,Spring Boot + WebServiceTemplate 会让你感觉非常舒适和自然。

分享:
扫描分享到社交APP
上一篇
下一篇