杰瑞科技汇

Java如何调用WebService?

目录

  1. 核心概念简介
    • 什么是 WebService?
    • 主要协议:SOAP vs. REST
  2. 使用 JAX-WS (Java API for XML Web Services) - 官方标准
    • 1 客户端调用方式一:wsimport 命令生成客户端代码
    • 2 客户端调用方式二:动态调用 Dispatch API (无需生成代码)
    • 3 服务端发布方式:使用 Endpoint
  3. 使用 Apache CXF - 强大的开源框架
    • 1 生成客户端 (与 JAX-WS 类似)
    • 2 使用 Spring 集成 CXF
  4. 使用 Spring Boot Starter 简化开发
    • 1 添加依赖
    • 2 配置和调用
  5. 访问 RESTful WebService (基于 HTTP/JSON)
    • 1 使用 RestTemplate (Spring 传统方式)
    • 2 使用 WebClient (Spring Reactive/推荐方式)
  6. 总结与对比

核心概念简介

什么是 WebService?

WebService 是一种跨编程语言和跨操作系统平台的远程调用技术,它使得不同系统(如 Java 应用和 .NET 应用)能够通过网络进行通信和数据交换。

Java如何调用WebService?-图1
(图片来源网络,侵删)

主要协议:SOAP vs. REST

特性 SOAP (Simple Object Access Protocol) REST (Representational State Transfer)
协议 严格基于 XML,通常通过 HTTP/SMTP 传输。 基于 HTTP 协议本身,没有严格的规范。
格式 强制使用 XML 灵活,常用 JSON,也支持 XML、HTML 等。
标准 有严格的官方标准(WSDL, SOAP, WS-*)。 架构风格,无官方标准。
性能 消息体积大,解析慢,性能相对较低。 消息体积小(尤其是 JSON),解析快,性能高。
安全性 内置强大的安全标准和事务支持。 安全性依赖于 HTTP 标准(如 OAuth, HTTPS)。
使用场景 企业级应用、金融、电信等对安全性、事务性要求高的场景。 公开 API、移动应用后端、微服务架构等。

本文重点:

  • SOAP WebService:主要使用 JAX-WSApache CXF
  • RESTful WebService:主要使用 Spring BootRestTemplateWebClient

方法一:使用 JAX-WS (Java API for XML Web Services)

JAX-WS 是 Java 官方提供的用于创建和调用 SOAP WebService 的 API,它是 Java EE 的一部分,现在也包含在 Jakarta EE 中。

1 客户端调用方式一:wsimport 命令生成客户端代码

这是最传统、最经典的方式,通过一个工具根据 WSDL (Web Service Description Language) 文件自动生成客户端调用代码。

步骤:

Java如何调用WebService?-图2
(图片来源网络,侵删)
  1. 获取 WSDL 文件地址 一个公共的天气查询服务 WSDL 地址:http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl

  2. 使用 wsimport 生成代码 打开命令行,执行以下命令:

    # -p: 指定生成的包名
    # -d: 指定代码存放的目录
    # -keep: 保留生成的源文件
    wsimport -p com.example.weather.client -d src/main/java -keep http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl

    执行后,会在 com.example.weather.client 包下生成一堆 Java 类(如 WeatherWSSoap, getWeather 等)。

  3. 在 Java 代码中调用

    package com.example.weather.client;
    public class WeatherClient {
        public static void main(String[] args) {
            // 1. 创建服务视图 (Service)
            // 参数是 wsdl 中定义的 <service name="...">
            WeatherWS service = new WeatherWS();
            // 2. 获取服务端点 (Port)
            // 参数是 wsdl 中定义的 <port name="..." binding="...">
            WeatherWSSoap port = service.getWeatherWSSoap();
            // 3. 调用具体的方法
            // 参数是 WSDL 中定义的请求参数
            String[] result = port.getWeather("北京", "");
            // 4. 处理结果
            if (result != null) {
                for (String s : result) {
                    System.out.println(s);
                }
            } else {
                System.out.println("查询失败");
            }
        }
    }

优点:

  • 代码类型安全,有 IDE 支持(代码提示、重构)。
  • 直接调用方法,感觉像调用本地方法一样。

缺点:

  • 需要一个额外的代码生成步骤。
  • 生成的代码可能很复杂,难以维护。

2 客户端调用方式二:动态调用 Dispatch API

如果你不想生成代码,或者 WSDL 文件不可用,可以使用 Dispatch API 进行动态调用。

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import java.net.URL;
public class DynamicWeatherClient {
    public static void main(String[] args) throws Exception {
        // 1. 创建 Service 实例
        URL wsdlUrl = new URL("http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl");
        QName qname = new QName("http://WebXml.com.cn/", "WeatherWS");
        Service service = Service.create(wsdlUrl, qname);
        // 2. 创建 Dispatch 实例,指定使用 SOAP 消息模式
        Dispatch<SOAPMessage> dispatch = service.createDispatch(
                new QName("http://WebXml.com.cn/", "WeatherWSSoap"),
                SOAPMessage.class,
                Service.Mode.MESSAGE);
        // 3. 创建并构造 SOAP 请求消息
        MessageFactory mf = MessageFactory.newInstance();
        SOAPMessage request = mf.createMessage();
        SOAPEnvelope envelope = request.getSOAPPart().getEnvelope();
        SOAPBody body = envelope.getBody();
        // 添加 SOAP Body 内容
        // 注意:这里的命名空间和元素名需要严格遵循 WSDL 定义
        QName bodyQName = new QName("http://WebXml.com.cn/", "getWeather");
        body.addBodyElement(bodyQName)
             .addAttribute(new QName("xmlns"), "http://WebXml.com.cn/")
             .addAttribute(new QName("cityName"), "北京");
        // 4. 发送请求并接收响应
        SOAPMessage response = dispatch.invoke(request);
        // 5. 处理响应
        response.writeTo(System.out);
    }
}

3 服务端发布方式:使用 Endpoint

如果你想快速发布一个简单的 SOAP 服务,可以使用 JAX-WS 提供的 Endpoint 类。

import javax.jws.WebService;
import javax.xml.ws.Endpoint;
// 1. 定义一个服务接口 (可选,但推荐)
@WebService
public interface HelloWorld {
    String sayHello(String name);
}
// 2. 实现服务接口
@WebService(endpointInterface = "com.example.HelloWorld")
public class HelloWorldImpl implements HelloWorld {
    @Override
    public String sayHello(String name) {
        return "Hello, " + name + "!";
    }
}
// 3. 发布服务
public class Publisher {
    public static void main(String[] args) {
        String address = "http://localhost:8080/hello";
        HelloWorldImpl helloWorld = new HelloWorldImpl();
        Endpoint.publish(address, helloWorld);
        System.out.println("Service published at: " + address);
    }
}

启动 Publisher 类后,你就可以通过地址 http://localhost:8080/hello?wsdl 访问 WSDL 文件了。


方法二:使用 Apache CXF

Apache CXF 是一个功能强大的开源框架,用于构建和开发 WebService,它支持 SOAP 和 REST,并且与 Spring 框架集成得非常好。

优点:

  • 功能强大,支持 WS-* 标准(如 WS-Security, WS-Addressing)。
  • 与 Spring 无缝集成,配置灵活。
  • 性能优于纯 JAX-WS 实现。

1 生成客户端 (与 JAX-WS 类似)

CXF 提供了 cxf-codegen-plugin Maven 插件,功能与 wsimport 类似。

<!-- pom.xml -->
<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/cxf</sourceRoot>
                <wsdlOptions>
                    <wsdlOption>
                        <wsdl>http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl</wsdl>
                    </wsdlOption>
                </wsdlOptions>
            </configuration>
            <goals>
                <goal>wsdl2java</goal>
            </goals>
        </execution>
    </executions>
</plugin>

执行 mvn generate-sources 后,代码会生成到 target/generated/cxf 目录下,后续调用方式与 JAX-WS 生成的代码完全一样。

2 使用 Spring 集成 CXF (服务端)

这是 CXF 最常用的方式,通过 Spring 来管理 Bean 和发布服务。

  1. 添加依赖

    <!-- pom.xml -->
    <dependencies>
        <!-- Spring Context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>
        <!-- CXF Core -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-core</artifactId>
            <version>3.4.5</version>
        </dependency>
        <!-- CXF JAX-RS & JAX-WS -->
        <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>
        <!-- CXF Spring Integration -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
            <version>3.4.5</version>
        </dependency>
    </dependencies>
  2. 配置 Spring XML (cxf-servlet.xml)

    <!-- src/main/resources/cxf-servlet.xml -->
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:jaxws="http://cxf.apache.org/jaxws"
           xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
        <!-- 1. 定义你的服务实现 Bean -->
        <bean id="helloWorldImpl" class="com.example.HelloWorldImpl"/>
        <!-- 2. 使用 JAX-WS 发布服务 -->
        <jaxws:endpoint id="helloWorld"
                        implementor="#helloWorldImpl"
                        address="/hello"/>
    </beans>
  3. 配置 web.xml

    <!-- web.xml -->
    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/ws/*</url-pattern>
    </servlet-mapping>

    服务地址为:http://localhost:8080/your-app-context/ws/hello


方法三:使用 Spring Boot Starter 简化开发

Spring Boot 极大地简化了 WebService 的开发和部署。

1 添加依赖

对于 SOAP (JAX-WS)

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<!-- CXF 提供了对 JAX-WS 的更好支持 -->
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
    <version>3.4.5</version>
</dependency>

对于 REST (Spring MVC)

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2 配置和调用 (SOAP)

  1. 服务实现

    @WebService(serviceName = "HelloWorld",
                targetNamespace = "http://example.com/",
                endpointInterface = "com.example.HelloWorld")
    @Component
    public class HelloWorldImpl implements HelloWorld {
        @Override
        public String sayHello(String name) {
            return "Hello from Spring Boot, " + name + "!";
        }
    }
  2. 配置类

    @Configuration
    public class CxfConfig {
        @Bean
        public ServletRegistrationBean<CXFServlet> cxfServlet() {
            return new ServletRegistrationBean<>(new CXFServlet(), "/ws/*");
        }
        @Bean(name = Bus.DEFAULT_BUS_ID)
        public SpringBus springBus() {
            return new SpringBus();
        }
        @Bean
        public Endpoint endpoint() {
            EndpointImpl endpoint = new EndpointImpl(springBus(), new HelloWorldImpl());
            endpoint.publish("/hello");
            return endpoint;
        }
    }

    服务地址:http://localhost:8080/ws/hello


方法四:访问 RESTful WebService (基于 HTTP/JSON)

现代 WebService 更多的是 RESTful 风格,Spring Boot 提供了非常便捷的工具。

1 使用 RestTemplate (Spring 传统方式)

RestTemplate 是一个同步的 HTTP 客户端,在 Spring 5 之前是标准。

  1. 配置 RestTemplate Bean

    @Configuration
    public class RestTemplateConfig {
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
    }
  2. 创建 DTO (Data Transfer Object)

    // 假设 API 返回 {"userId": 1, "id": 1, "title": "...", "completed": false}
    public class Todo {
        private int userId;
        private int id;
        private String title;
        private boolean completed;
        // Getters and Setters...
    }
  3. 在 Service 中调用

    @Service
    public class TodoService {
        @Autowired
        private RestTemplate restTemplate;
        private static final String TODO_URL = "https://jsonplaceholder.typicode.com/todos/1";
        public Todo getTodoById(int id) {
            // 使用 getForObject,直接将 JSON 映射到 Todo 对象
            return restTemplate.getForObject(TODO_URL + "/" + id, Todo.class);
        }
    }

2 使用 WebClient (Spring Reactive/推荐方式)

WebClient 是 Spring 5 引入的、非阻塞的、响应式的 HTTP 客户端,是 RestTemplate 的现代替代品。

  1. 配置 WebClient Bean

    @Configuration
    public class WebClientConfig {
        @Bean
        public WebClient webClient() {
            return WebClient.create();
        }
    }
  2. 在 Service 中调用

    @Service
    public class TodoServiceWebClient {
        @Autowired
        private WebClient webClient;
        private static final String TODO_URL = "https://jsonplaceholder.typicode.com/todos";
        public Mono<Todo> getTodoByIdReactive(int id) {
            // 返回一个 Mono,代表一个异步的、可能包含 0-1 个值的流
            return webClient.get()
                    .uri(TODO_URL + "/{id}", id)
                    .retrieve() // 发出请求并获取响应
                    .bodyToMono(Todo.class); // 将响应体反序列化为 Todo 对象
        }
        // 同步调用方式
        public Todo getTodoByIdSync(int id) {
            return getTodoByIdReactive(id).block(); // block() 会阻塞线程,直到结果返回
        }
    }

WebClient 的优势:

  • 非阻塞:在高并发场景下,能以更少的线程处理更多的请求,性能更高。
  • 响应式编程:与 Project Reactor 和 RxJava 等库无缝集成,适合构建异步、事件驱动的应用。

总结与对比

方法 协议 优点 缺点 适用场景
JAX-WS (wsimport) SOAP 类型安全,IDE 友好 需要代码生成,代码臃肿 传统企业应用,与遗留系统集成
JAX-WS (Dispatch) SOAP 动态灵活,无需代码 编码复杂,容易出错 WSDL 动态变化,或无法预知服务接口时
Apache CXF SOAP/REST 功能强大,支持标准多,Spring 集成好 学习曲线稍陡 企业级应用,需要高级 WS-* 特性的项目
Spring Boot (SOAP) SOAP 配置简单,快速启动 对复杂 SOAP 支持可能不如 CXF 新的 Spring Boot 微服务项目,需要 SOAP 时
RestTemplate REST 简单易用,同步模型 阻塞式,性能有限 简单的 REST 客户端调用,或旧项目维护
WebClient REST 非阻塞,高性能,响应式 异步编程模型需要适应 新项目,特别是高并发、微服务架构

如何选择?

  • 必须对接一个传统的 SOAP 服务?
    • 如果只是客户端调用,优先考虑 JAX-WS wsimport,因为它最简单直接。
    • 如果服务端需要发布或需要更复杂的功能,选择 Apache CXFSpring Boot + CXF
  • 开发一个新的、现代化的服务?
    • 首选 RESTful 风格
    • 在 Spring 生态中,WebClient 是未来的趋势,特别是对于构建高性能的系统。RestTemplate 虽然仍然可用,但已被标记为不推荐在新项目中使用。
    • 如果项目不是基于 Spring,可以使用其他库如 OkHttpApache HttpClient 来调用 REST API。

希望这份详细的指南能帮助你理解在 Java 中访问 WebService 的各种方法!

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