杰瑞科技汇

Java webservice异步如何实现?

在传统的同步 WebService 调用中,客户端发起请求后会一直阻塞,直到收到服务端的响应,如果服务端处理时间较长,客户端的线程就会被长时间占用,导致资源浪费和用户体验下降。

Java webservice异步如何实现?-图1
(图片来源网络,侵删)

异步 WebService 调用则允许客户端在发送请求后立即释放线程,可以去执行其他任务,而不必等待服务端的响应,当服务端处理完成后,会通过某种回调机制将结果返回给客户端。

核心概念

  1. 异步非阻塞:客户端线程不等待,立即返回。
  2. 回调:这是异步模式的核心,客户端需要提供一个“回调地址”(Callback Address),服务端在处理完请求后,会主动向这个地址发送响应结果。
  3. Future/Promise 模式:客户端发起请求后,会立即收到一个“未来结果”的对象(如 java.util.concurrent.Future),客户端可以稍后通过这个对象去检查是否完成,并获取最终结果。

实现方式

在 Java 生态中,实现 WebService 异步调用主要有以下几种方式,它们分别对应不同的技术栈和时代:

JAX-WS (Java API for XML Web Services) - 标准方式

JAX-WS 是 Java 官方标准的 WebService 技术,它本身就内置了对异步调用的支持,它主要通过 FutureCallback 两种方式实现。

场景:假设我们有一个简单的 WebService

服务端接口 (HelloService)

Java webservice异步如何实现?-图2
(图片来源网络,侵删)
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
// @WebService 表示这是一个 WebService 接口
@WebService
// @SOAPBinding 指定 SOAP 协议风格
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface HelloService {
    // 同步方法
    @WebMethod
    String sayHello(String name);
    // 异步方法,返回 Future
    @WebMethod
    java.util.concurrent.Future<String> sayHelloAsync(String name);
    // 异步方法,使用回调
    @WebMethod
    void sayHelloAsync(String name, javax.xml.ws.AsyncHandler<java.lang.String> handler);
}

服务端实现 (HelloServiceImpl)

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import java.util.concurrent.Future;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.xml.ws.AsyncHandler;
@WebService(endpointInterface = "com.example.HelloService")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class HelloServiceImpl implements HelloService {
    // 使用线程池来模拟耗时操作,避免阻塞服务端线程
    private final ExecutorService executor = Executors.newCachedThreadPool();
    @Override
    public String sayHello(String name) {
        System.out.println("同步方法被调用,线程: " + Thread.currentThread().getName());
        try {
            // 模拟耗时操作
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Hello, " + name + " (同步调用)";
    }
    @Override
    public Future<String> sayHelloAsync(String name) {
        System.out.println("异步方法(Future)被调用,线程: " + Thread.currentThread().getName());
        // 在后台线程中执行任务
        AsyncHelloTask task = new AsyncHelloTask(name);
        executor.execute(task);
        return task;
    }
    @Override
    public void sayHelloAsync(String name, AsyncHandler<String> handler) {
        System.out.println("异步方法(Callback)被调用,线程: " + Thread.currentThread().getName());
        // 在后台线程中执行任务,完成后调用 handler
        executor.execute(() -> {
            try {
                Thread.sleep(3000);
                String result = "Hello, " + name + " (异步回调)";
                System.out.println("任务完成,准备回调...");
                // 处理完成,调用 handleResponse 方法
                handler.handleResponse(new javax.xml.ws.Response<>(result, null));
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
    // 用于 Future 方式的内部任务类
    private static class AsyncHelloTask implements Future<String> {
        private String name;
        private volatile boolean isDone = false;
        private volatile String result;
        private volatile Exception exception;
        public AsyncHelloTask(String name) {
            this.name = name;
        }
        @Override
        public boolean cancel(boolean mayInterruptIfRunning) {
            return false;
        }
        @Override
        public boolean isCancelled() {
            return false;
        }
        @Override
        public boolean isDone() {
            return isDone;
        }
        @Override
        public String get() throws InterruptedException, java.util.concurrent.ExecutionException {
            // 如果任务没完成,当前线程会等待
            while (!isDone) {
                Thread.sleep(100);
            }
            if (exception != null) {
                throw new java.util.concurrent.ExecutionException(exception);
            }
            return result;
        }
        @Override
        public String get(long timeout, java.util.concurrent.TimeUnit unit)
                throws InterruptedException, java.util.concurrent.ExecutionException, java.util.concurrent.TimeoutException {
            // 带超时的等待
            long endTime = System.currentTimeMillis() + unit.toMillis(timeout);
            while (!isDone && System.currentTimeMillis() < endTime) {
                Thread.sleep(100);
            }
            if (!isDone) {
                throw new java.util.concurrent.TimeoutException();
            }
            if (exception != null) {
                throw new java.util.concurrent.ExecutionException(exception);
            }
            return result;
        }
        public void setResult(String result) {
            this.result = result;
            this.isDone = true;
        }
    }
}

客户端调用示例

import javax.xml.namespace.QName;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPFaultException;
import java.util.concurrent.Future;
import java.util.concurrent.ExecutionException;
public class JAXWSAsyncClient {
    public static void main(String[] args) {
        try {
            // 1. 创建 Service 实例 (wsimport 生成的代码会简化这一步)
            URL wsdlUrl = new URL("http://localhost:8080/HelloService?wsdl");
            QName qname = new QName("http://example.com/", "HelloServiceService");
            Service service = Service.create(wsdlUrl, qname);
            HelloService helloService = service.getPort(HelloService.class);
            System.out.println("--- 开始异步调用 (Future 方式) ---");
            // Future 方式
            Future<String> futureResult = helloService.sayHelloAsync("Future User");
            // 在等待结果期间,客户端可以做其他事情
            System.out.println("请求已发送,客户端线程未被阻塞,可以继续做其他工作...");
            for (int i = 0; i < 5; i++) {
                System.out.println("客户端正在执行其他任务... " + i);
                Thread.sleep(500);
            }
            // 获取结果,如果结果未准备好,这里会阻塞
            String resultFromFuture = futureResult.get();
            System.out.println("Future 方式收到结果: " + resultFromFuture);
            System.out.println("\n--- 开始异步调用 (Callback 方式) ---");
            // Callback 方式
            helloService.sayHelloAsync("Callback User", new AsyncHandler<String>() {
                @Override
                public void handleResponse(Response<String> response) {
                    try {
                        String result = response.get();
                        System.out.println("Callback 方式收到结果: " + result);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            System.out.println("Callback 请求已发送,客户端线程未被阻塞...");
            // 主线程需要保持存活,否则回调可能无法执行
            // 在真实应用中,主线程可能是一个 Web 容器或消息监听器
            Thread.sleep(5000); // 等待回调完成
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

JAX-WS 异步总结:

  • 优点: 是 Java 标准的一部分,无需额外依赖。
  • 缺点: 配置和代码相对繁琐,回调处理需要手动实现。wsimport 工具会生成客户端辅助类,简化部分工作,但底层逻辑依然如此。

Spring Boot + Spring WebService - 现代方式

Spring 对 JAX-WS 和 JAX-RS 都提供了优秀的支持,使得异步调用变得更加简洁和现代化。

Java webservice异步如何实现?-图3
(图片来源网络,侵删)

场景:基于 Spring Boot 的异步 WebService

服务端 (使用 @Async)

Spring 的 @Async 注解是实现异步调用的利器。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import javax.jws.WebService;
import java.util.concurrent.Executor;
@SpringBootApplication
@EnableAsync // 启用异步方法支持
public class SpringWsApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringWsApplication.class, args);
    }
    // 配置一个线程池用于异步任务
    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("WsAsync-");
        executor.initialize();
        return executor;
    }
}
// WebService 接口
import org.springframework.scheduling.annotation.Async;
@WebService
public interface SpringHelloService {
    String sayHello(String name);
    // 使用 @Async 注解,Spring 会自动在后台线程中执行此方法
    @Async
    String sayHelloAsync(String name);
}
// WebService 实现
import org.springframework.stereotype.Service;
@Service
@WebService(serviceName = "SpringHelloService", endpointInterface = "com.example.SpringHelloService")
public class SpringHelloServiceImpl implements SpringHelloService {
    @Override
    public String sayHello(String name) {
        System.out.println("同步调用: " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Hello, " + name + " (Spring 同步)";
    }
    @Override
    @Async
    public String sayHelloAsync(String name) {
        System.out.println("异步调用: " + Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Hello, " + name + " (Spring 异步)";
    }
}

客户端 (使用 WebTemplate)

Spring 提供了 WebTemplate(类似于 RestTemplate)来简化 WebService 调用。

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.web.client.RestTemplateBuilder;
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;
import org.springframework.ws.soap.client.core.SoapActionCallback;
@Configuration
public class WsClientConfig {
    @Bean
    public Jaxb2Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        // 需要指定服务端接口的包名
        marshaller.setPackagesToScan("com.example");
        return marshaller;
    }
    @Bean
    public WebServiceTemplate webServiceTemplate(Jaxb2Marshaller marshaller) {
        WebServiceTemplate template = new WebServiceTemplate(marshaller);
        template.setDefaultUri("http://localhost:8080/ws/springHelloService");
        return template;
    }
    @Bean
    public CommandLineRunner run(WebServiceTemplate webServiceTemplate) {
        return args -> {
            System.out.println("--- Spring WebService 异步调用 ---");
            // 同步调用
            // String response = (String) webServiceTemplate.marshalSendAndReceive("World", new SoapActionCallback(""));
            // 异步调用
            // 发送请求并立即返回一个 Future 对象
            java.util.concurrent.Future<Object> future = webServiceTemplate.sendAndReceive(
                "World",
                message -> {
                    message.setSoapAction("http://example.com/springHelloService/sayHelloAsync");
                    // 这里可以设置请求体
                },
                response -> {
                    // 这里是响应回调,可以处理响应
                    return response.getPayload(); // 返回响应结果
                }
            );
            System.out.println("Spring 异步请求已发送,客户端未被阻塞...");
            // 可以做其他事情...
            for (int i = 0; i < 3; i++) {
                System.out.println("客户端忙于其他事务...");
                Thread.sleep(500);
            }
            // 获取结果
            try {
                String result = (String) future.get();
                System.out.println("Spring 异步调用收到结果: " + result);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        };
    }
}

Spring WebService 异步总结:

  • 优点: 与 Spring 生态系统无缝集成,代码更简洁,通过 @Async 和线程池配置,管理非常方便。
  • 缺点: 需要 Spring Boot 环境,对于轻量级应用可能显得过重。

JAX-RS (Java API for RESTful Web Services) - REST API 方式

对于 RESTful API,异步调用是现代 Web 应用的标配,通常使用 CompletableFuture 和响应式编程(如 Project Reactor, RxJava)来实现。

场景:Spring Boot + JAX-RS (例如使用 Jersey 或 RESTEasy)

服务端 (返回 CompletableFuture)

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@RestController
public class AsyncResourceController {
    private final Executor executor = Executors.newFixedThreadPool(10);
    @GetMapping("/sync/hello/{name}")
    public String sayHelloSync(@PathVariable String name) throws InterruptedException {
        Thread.sleep(3000);
        return "Hello, " + name + " (REST 同步)";
    }
    @GetMapping("/async/hello/{name}")
    public CompletableFuture<String> sayHelloAsync(@PathVariable String name) {
        System.out.println("接收到请求,线程: " + Thread.currentThread().getName());
        // 将耗时任务提交到线程池,并返回一个 CompletableFuture
        return CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new IllegalStateException(e);
            }
            return "Hello, " + name + " (REST 异步)";
        }, executor);
    }
}

客户端 (使用 WebClient)

Spring 的 WebClient 是进行异步 HTTP 请求的现代推荐方式。

import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Configuration
public class WebClientConfig {
    @Bean
    public CommandLineRunner run(WebClient.Builder builder) {
        return args -> {
            WebClient webClient = builder.baseUrl("http://localhost:8080").build();
            System.out.println("--- REST API 异步调用 ---");
            // 异步调用
            Mono<String> asyncResponse = webClient.get()
                .uri("/async/hello/WebClientUser")
                .retrieve()
                .bodyToMono(String.class);
            System.out.println("REST 异步请求已发送...");
            // 订阅结果,当结果到达时执行
            asyncResponse.subscribe(
                result -> System.out.println("收到异步结果: " + result),
                error -> System.err.println("发生错误: " + error.getMessage())
            );
            System.out.println("客户端可以继续执行其他逻辑...");
            // 主线程不等待
        };
    }
}

JAX-RS 异步总结:

  • 优点: RESTful API 的标准异步实践,性能高,非阻塞(如果使用响应式编程如 WebFlux),是现代微服务架构的首选。
  • 缺点: 主要用于 REST API,不适用于传统的 SOAP WebService。

总结与对比

特性 JAX-WS (标准) Spring WebService JAX-RS (REST)
技术类型 SOAP SOAP REST
异步实现 Future<T>, AsyncHandler<T> @Async, WebServiceTemplate (Future/Promise) CompletableFuture<T>, Reactive (Mono/Flux)
易用性 较低,代码繁琐 高,与 Spring 生态集成 高,现代 API 设计
性能 受限于线程池模型 受限于线程池模型 非阻塞模型,性能极高
适用场景 遗留系统、需要严格 SOAP 协议的企业集成 基于 Spring 的 SOAP 服务开发 现代微服务、高并发 REST API
依赖 JDK 内置 Spring Framework Spring Boot / Jersey / RESTEasy

如何选择?

  • 如果你在维护一个旧的 SOAP 项目,并且需要添加异步功能,JAX-WS 是你的不二之选。
  • 如果你的新项目基于 Spring 生态,并且需要构建 SOAP 服务,使用 Spring WebService 会让你事半功倍。
  • 如果你在构建新的、高性能的、现代化的微服务,JAX-RS (REST) 是绝对的主流和最佳实践,特别是结合响应式编程时。
分享:
扫描分享到社交APP
上一篇
下一篇