- JAX-WS (Java API for XML Web Services):这是早期和非常成熟的基于 XML 的标准,我们通常说的 "Axis", "CXF" 等框架都是实现 JAX-WS 规范的。
- JAX-RS (Java API for RESTful Web Services):这是目前更主流、更轻量级的基于 REST 架构风格的标准,我们通常说的 "Jersey", "RESTEasy", "Spring MVC" 都是实现 JAX-RS 规范的。
下面我将为你详细讲解这两种方式,并提供完整的文件结构和代码示例。

场景设定:一个简单的文件上传服务
我们将创建一个 WebService,它提供一个端点,允许客户端上传一个文件,服务端接收并保存在指定目录中,这个例子能很好地展示 WebService 如何处理二进制文件。
使用 JAX-WS (SOAP 方式)
JAX-WS 通常使用 SOAP 协议,消息格式是 XML,处理文件时,文件内容会被 Base64 编码后嵌入到 SOAP 消息的 <attachment> 中。
项目结构
jax-ws-file-upload/
├── pom.xml
├── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── service
│ │ ├── FileUploadWS.java // WebService 接口
│ │ ├── FileUploadWSImpl.java // WebService 实现类
│ │ └── Publisher.java // 发布服务的工具类
│ └── webapp
│ └── WEB-INF
│ └── web.xml
核心代码
a. pom.xml (使用 Maven)
需要引入 cxf-rt-frontend-jaxws 和 cxf-rt-transports-http 依赖。
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>jax-ws-file-upload</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<cfx.version>3.4.5</cfx.version>
</properties>
<dependencies>
<!-- CXF JAX-WS 核心依赖 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cfx.version}</version>
</dependency>
<!-- CXF HTTP 传输依赖 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cfx.version}</version>
</dependency>
<!-- Servlet API (如果使用 Tomcat 等容器) -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>
</project>
b. FileUploadWS.java (WebService 接口)
使用 @WebService 注解来定义一个 WebService。

package com.example.service;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.bind.annotation.XmlMimeType;
import javax.activation.DataHandler;
import java.io.InputStream;
// @WebService 定义这是一个 WebService 接口
@WebService
// @SOAPBinding 定义 SOAP 消息风格,默认是 DOCUMENT
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT)
public interface FileUploadWS {
// @WebMethod 定义这是一个 WebService 方法
// @WebParam 定义方法的参数名
// @XmlMimeType 和 DataHandler 结合,用于处理附件(二进制数据)
@WebMethod
String uploadFile(
@WebParam(name = "fileName") String fileName,
@WebParam(name = "fileContent") @XmlMimeType("application/octet-stream") DataHandler fileContent);
}
c. FileUploadWSImpl.java (WebService 实现类)
实现接口,并处理文件保存逻辑。
package com.example.service;
import javax.activation.DataHandler;
import javax.jws.WebService;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
@WebService(endpointInterface = "com.example.service.FileUploadWS") // 指向接口
public class FileUploadWSImpl implements FileUploadWS {
@Override
public String uploadFile(String fileName, DataHandler fileContent) {
// 1. 创建保存文件的目录
String uploadDir = "C:/temp/uploads/"; // 请确保此目录存在且有写入权限
File dir = new File(uploadDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 2. 生成唯一文件名,防止覆盖
String uniqueFileName = UUID.randomUUID().toString() + "_" + fileName;
File uploadedFile = new File(uploadDir, uniqueFileName);
// 3. 保存文件
try (InputStream is = fileContent.getInputStream();
FileOutputStream fos = new FileOutputStream(uploadedFile)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
return "文件上传成功!保存在: " + uploadedFile.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
return "文件上传失败: " + e.getMessage();
}
}
}
d. Publisher.java (发布服务)
这是一个独立的 Java 应用,可以直接运行来发布 WebService。
package com.example.service;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
public class Publisher {
public static void main(String[] args) {
// 1. 创建 JaxWsServerFactoryBean
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
// 2. 设置服务实现类
factory.setServiceBean(new FileUploadWSImpl());
// 3. 设置服务地址
factory.setAddress("http://localhost:8080/ws/fileupload");
// 4. 创建并启动服务
factory.create();
System.out.println("WebService 已成功发布,访问地址为: http://localhost:8080/ws/fileupload?wsdl");
}
}
e. web.xml (部署到 Web 容器)
如果你想将服务部署到 Tomcat 等 Web 容器中,需要配置 web.xml。
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<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>
</web-app>
如何使用
- 发布服务:运行
Publisher.java的main方法。 - 验证 WSDL:在浏览器中访问
http://localhost:8080/ws/fileupload?wsdl,如果能看到 XML 内容,说明发布成功。 - 客户端调用:你需要一个 SOAP 客户端(如 SoapUI,或使用
wsimport生成的 Java 客户端)来调用这个服务,客户端需要构造一个包含文件附件的 SOAP 请求。
使用 JAX-RS (REST 方式)
JAX-RS 是目前更流行的方式,它使用 HTTP 协议,通常以 JSON 或 XML 交换数据,文件上传通常使用 multipart/form-data 格式。

项目结构
jax-rs-file-upload/
├── pom.xml
├── src
│ └── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── resource
│ │ └── FileResource.java // RESTful 资源类
│ └── webapp
│ └── WEB-INF
│ └── web.xml
核心代码
a. pom.xml (使用 Maven)
需要引入 jersey-server, jersey-container-servlet, 和处理文件上传的 jersey-media-multipart。
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>jax-rs-file-upload</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jersey.version>2.37</jersey.version>
</properties>
<dependencies>
<!-- Jersey 核心依赖 -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- Jersey 文件上传支持 -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey.version}</version>
</dependency>
<!-- Servlet API (如果使用 Tomcat 等容器) -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>
</project>
b. FileResource.java (RESTful 资源类)
使用 @Path, @POST, @FormDataParam 等注解来定义资源。
package com.example.resource;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;
@Path("/files")
public class FileResource {
@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(
// @FormDataParam 用于接收 multipart/form-data 中的字段
@FormDataParam("file") InputStream uploadedInputStream,
@FormDataParam("file") FormDataContentDisposition fileDetail) {
// 1. 检查文件是否存在
if (uploadedInputStream == null || fileDetail == null) {
return Response.status(Response.Status.BAD_REQUEST).entity("请上传一个文件").build();
}
// 2. 创建保存文件的目录
String uploadDir = "C:/temp/uploads/"; // 请确保此目录存在且有写入权限
File dir = new File(uploadDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 3. 生成唯一文件名
String uniqueFileName = UUID.randomUUID().toString() + "_" + fileDetail.getFileName();
File uploadedFile = new File(uploadDir, uniqueFileName);
// 4. 保存文件
try (FileOutputStream out = new FileOutputStream(uploadedFile)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = uploadedInputStream.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
String output = "文件 '" + uniqueFileName + "' 上传成功!保存在: " + uploadedFile.getAbsolutePath();
return Response.status(Response.Status.OK).entity(output).build();
} catch (IOException e) {
e.printStackTrace();
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("文件上传失败: " + e.getMessage()).build();
}
}
}
c. web.xml (部署到 Web 容器)
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.example.resource</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
</web-app>
如何使用
- 部署服务:将项目打包成 WAR 文件,部署到 Tomcat 或其他支持 Servlet 的容器中。
- 验证服务:在浏览器中访问
http://localhost:8080/your-app-name/api/files/upload,会返回 405 Method Not Allowed,因为这是一个 POST 接口,这是正常的。 - 客户端调用:
- 使用
curl命令行工具:curl -X POST -F "file=@/path/to/your/local/file.txt" http://localhost:8080/your-app-name/api/files/upload
- 使用 Postman:
- 选择 POST 方法。
- 输入 URL:
http://localhost:8080/your-app-name/api/files/upload - 在 "Body" 选项卡中,选择 "form-data"。
- 在 "Key" 列中输入
file,在 "Type" 列中选择 "File",然后点击 "Select Files" 选择你的本地文件。 - 点击 "Send" 按钮。
- 使用
总结与对比
| 特性 | JAX-WS (SOAP) | JAX-RS (REST) |
|---|---|---|
| 协议 | SOAP (基于 XML) | HTTP (GET, POST, PUT, DELETE) |
| 数据格式 | XML (严格定义的 Schema) | JSON, XML, Text 等 (更灵活) |
| 风格 | 面向服务,有明确的接口契约 | 面向资源,风格更轻量、无状态 |
| 文件处理 | 通过 SOAP Attachment (如 MTOM 优化) | 通过 multipart/form-data |
| 标准 | Java EE 标准的一部分 | Java EE 标准的一部分 |
| 适用场景 | 企业级应用,需要高安全性、事务、ACID 特性,与旧系统集成 | Web 和移动应用 API,前后端分离,需要高性能和易用性 |
| 框架示例 | Apache CXF, Metro | Jersey, RESTEasy, Spring MVC (也支持 REST) |
如何选择?
- 如果你的项目是传统的企业级应用,需要与现有的 SOAP 服务集成,或者对事务和安全有非常严格的要求,可以选择 JAX-WS。
- 如果你在构建新的 Web 应用或移动 App 的后端 API,追求开发效率、性能和灵活性,JAX-RS (REST) 是绝对的首选,目前绝大多数新的 Java WebService 项目都采用 REST 风格。
