Java EE Web Service实战:SOAP与REST选型及JAX-WS核心机制
干 Java 这行十来年Web Service 这个词几乎贯穿了整个职业生涯。从最早的 SOAP 和 WSDL到后来 REST 风格大行其道再到微服务阶段 HTTP JSON 成为默认选择底层那套东西其实一直没变。很多人一听到 JAVA EE 里的 Web Service 就头大觉得规范多、配置繁琐、概念抽象但其实只要把“它到底解决什么问题”想清楚整个知识体系就顺了。这篇东西不是教科书式的复读我尽量用做项目、踩坑、干活的口吻把 JAVA EE 应用程序中实现 Web Service 服务的基础理论拆开揉碎。适合正在学后端开发、准备做系统集成、或者被老项目里 SOAP 接口折磨过的朋友。看完你至少能搞清楚三件事SOAP 和 REST 到底怎么选、JAX-WS 的核心机制是什么、部署上线后有哪些坑等着你。1. 重新理解 Web Service它解决的是系统之间的“对话”问题1.1 为什么 JAVA EE 里会有 Web Service 这个概念先说一个很实际的场景。假设你所在的公司有一套用了十年的 ERP 系统数据库是 Oracle逻辑层是 Java跑在专用的内网服务器上。现在新上了一个 CRM 系统需要实时读取 ERP 里的客户订单状态。问题来了两套系统的语言不一样、数据格式不一样、部署环境也不一样怎么让 CRM 稳定地拿到 ERP 的数据这就是 Web Service 存在的理由。它提供了一套跨语言、跨平台、跨网络的调用规范让 A 系统可以像调用本地方法一样调用 B 系统的功能。JAVA EE 作为企业级开发的标准体系很早就把 Web Service 纳入自己的规范版图也就是 JAX-WSSOAP 方向和 JAX-RSREST 方向。注意它们是规范不是具体的框架。就像 JDBC 是 Java 访问数据库的规范MySQL 驱动、PostgreSQL 驱动是具体实现JAX-WS 和 JAX-RS 也需要具体的实现库来落地比如 Metro、Apache CXF、Jersey、RESTEasy 这些。这里有个常见的误解很多人以为 JAVA EE 里的 Web Service 就等于 SOAP XML。其实在 Java EE 5 之后REST 风格的 JAX-RS 就已经成为官方规范的一部分。到了今天的 Jakarta EE 时代这两条路线依然并行存在。理解这一点你就知道为什么有的老项目还在用 .wsdl 文件有的新项目全是一堆 Path 注解。1.2 Web Service 的三个核心角色服务提供者、服务消费者、服务描述不管 SOAP 还是 RESTWeb Service 的基本交互模型都是围绕着三个角色展开的。服务提供者Provider把业务功能暴露成网络可调用的接口它定义了“你能调我什么方法、传什么参数、返回什么结果”。服务消费者Consumer是调用方它不需要关心服务的内部实现只需要知道怎么发请求、怎么解析响应。服务描述Description则是中间那座桥SOAP 用 WSDL 文件描述REST 用 OpenAPI / Swagger 描述作用都是让消费者能够“按图索骥”。你可以把服务描述理解成电器说明书。消费者不需要知道电器内部怎么走线、怎么焊板子只需要知道插哪个孔、按哪个按钮就行。WSDL 和 OpenAPI 就是这份说明书只不过它不止给人看更多是让程序来解析。在 JAVA EE 的实际应用中服务提供者通常是一个部署在应用服务器上的 Web 应用它通过 Servlet 容器接收 HTTP 请求再把请求交给 JAX-WS 或 JAX-RS 的运行时处理。服务消费者可以是另一个 Java 应用也可以是 .NET、Python、Node.js 写的程序只要它能构造出符合约定的 XML 或 JSON 请求就行。这也是 Web Service 最核心的价值异构系统之间的互操作。2. SOAP 还是 REST先搞明白选型背后的逻辑2.1 SOAP 的理论模型信封、契约、消息SOAPSimple Object Access Protocol名字里带个 Simple但实际使用起来并不简单。它的核心思路是把调用信息封装在一个 XML 信封Envelope里通过 HTTP、JMS 甚至 SMTP 等传输协议发送给对方。信封分两部分Header 放事务控制、安全令牌、路由信息等元数据Body 放具体的业务参数和响应结果。SOAP 的一个核心特征是“契约优先”。也就是说在写代码之前你得先定义 WSDL 文件把服务接口、消息格式、端口绑定全部约定好。这个文件就是服务双方的“合同”一旦发布任何一方的改动都可能造成合同破裂。WSDL 文档的结构其实不复杂核心就是五个部分types定义消息中用到的数据类型一般用 XML Schemamessage定义消息的抽象格式portType定义服务提供的操作列表相当于 Java 里的接口binding把 portType 绑定到具体的传输协议和消息格式service指定服务的访问地址也就是 endpoint在实际开发中很少有人手工写 WSDL大多是先写 Java 接口然后由工具生成 WSDL。但你要能读懂它因为联调的时候对方发来一个 .wsdl 文件你能一眼看出接口定义在哪、服务地址在哪、参数类型会不会对不上这是排查问题的基础能力。2.2 REST 的理论模型资源、表现、状态转移RESTRepresentational State Transfer是 Roy Fielding 在博士论文里提出来的架构风格跟 SOAP 不一样它不是一个协议而是一组设计原则。核心思想是把业务数据抽象成资源每个资源有一个 URL通过 HTTP 动词表达操作GET 获取、POST 创建、PUT 更新、DELETE 删除。JAVA EE 里实现 REST 风格的规范是 JAX-RS它把资源类Resource Class、资源方法Resource Method、注解驱动如 Path、GET、Produces这套机制定义得非常清晰。相比 SOAP 那种“一个方法做一件事”的模式REST 更强调“一个资源可以有多种操作”的思路。举个例子。一个 SOAP 服务可能会定义一个 getOrderById 方法客户端要传一个 orderId 参数。REST 风格下同样的功能就是 GET /orders/{orderId}更直观也和 HTTP 的语义完全吻合。2.3 选型对比与实战建议很多初学者纠结到底学 SOAP 还是 REST其实在真实项目里这个选择通常不是技术偏好问题而是行业和场景决定的。SOAP 仍然活跃在金融、电信、政务等传统行业的核心系统里。原因很简单这些系统对事务性、安全性、可靠性要求极高SOAP 有 WS-Security、WS-AtomicTransaction 等一系列重量级规范加持很多东西是现成的。而且老系统的存量接口大部分是 SOAP改造成本太高新系统为了对接只能继续用 SOAP。REST 在互联网、移动端、快速迭代的业务系统里是绝对主流。它轻量、直观、缓存友好JSON 的解析效率在大多数场景下也优于 XML。如果你做的是新项目没有必须对接 SOAP 的历史包袱优先选 REST。我个人的经验是团队技术栈偏 Java 服务端、系统属于企业内部集成、对事务和安全有强制要求可以考虑 SOAP。如果是面向外部开放 API、前后端分离、追求快速迭代直接 REST 不要犹豫。最关键的一点是不要设计一套对外的通用接口同时又用 SOAP 又用 REST维护成本会翻倍。3. JAX-WS 的理论骨架SEI、消息链与 WSDL 的协作关系3.1 SEI 和 SIB接口与实现的分离之道JAX-WS 里有两个必须搞清楚的概念SEIService Endpoint Interface服务端点接口和 SIBService Implementation Bean服务实现类。SEI 是用 Java 接口定义服务方法的入口SIB 是真正写业务逻辑的类。为什么非要分这两层因为 JAX-WS 运行时需要用 SEI 来生成 WSDL 和对应的 XML Schema。如果你把方法定义和业务实现写在同一个类里理论上可行但工具生成契约的时候很难优雅地区分“哪些方法要对外暴露、哪些方法是内部辅助方法”。接口和实现分离之后暴露哪些方法一目了然也能防止业务类里不小心混入一些不该公开的内部逻辑。写 SEI 的时候有几点要注意。第一接口和实现类都要用 WebService 注解标注接口上可以指定 targetNamespace实现类里要设置 endpointInterface 指回接口的全限定名。第二方法要用 WebMethod 标注方法参数可以用 WebParam 指定名字不然生成的 WSDL 里参数名可能变成 arg0、arg1 这种鬼样子。第三返回值最好用简单类型、JavaBean或者集合尽量避免直接返回 org.w3c.dom.Document 这类底层 API否则序列化会非常痛苦。3.2 SOAP Handler消息在进出边界时的“安检通道”JAX-WS 提供了一个很实用的扩展机制SOAP Handler。它有点像 Servlet 里的 Filter可以在消息进入服务端之前、服务端返回结果之后对 SOAP 消息做拦截和处理。Handler 能干什么最常见的是打日志。把请求报文和响应报文原样记录下来这在联调和排障时是救命稻草。尤其是对方不承认自己传了某个参数或者返回值莫名其妙多了一个节点把日志翻出来一看就清楚了。其次是做统一的信息头处理比如往 Header 里塞认证令牌、链路追踪 ID有些老系统的认证信息就是放在 SOAP Header 里的自定义元素里的。写 Handler 的时候要注意Handler 链的执行顺序是有讲究的。JAX-WS 规范定义了 Logical Handler 和 SOAP Handler 两种前者只操作消息上下文拿不到 SOAP 报文后者可以完整读写 SOAP 消息。如果你需要在服务端修改请求内容用 SOAP Handler。如果你只是想在调用前后做些检查用 Logical Handler 更轻量。另外Handler 里抛异常要特别小心抛出去之后整个调用链就断了最好用 try-catch 包一层记录日志后决定是继续还是中断。3.3 消息传递格式SOAP 报文长什么样理解 SOAP 报文结构对排障太重要了。一个典型的 SOAP 请求报文大致长这样?xml version1.0 encodingUTF-8? soap:Envelope xmlns:soaphttp://schemas.xmlsoap.org/soap/envelope/ soap:Header auth:token xmlns:authhttp://example.com/authabc123/auth:token /soap:Header soap:Body ns:sayHello xmlns:nshttp://example.com/greeting ns:name张三/ns:name /ns:sayHello /soap:Body /soap:Envelope看到这个报文你能很直观地理解之前说的结构Envelope 是信封Header 是扩展信息的容器Body 是业务内容的载体。很多老工程师拿到一个 SOAP 报错第一反应不是去翻代码而是直接看报文里的 Fault 节点那里面的 faultcode 和 faultstring 往往直接指明了问题方向。这里有一个字符编码的坑必须提。SOAP 报文默认是 XML 编码如果你的服务端和客户端两侧的字符编码不一致比如服务端用 UTF-8客户端用 GBK中文参数就会乱码。最稳妥的做法是在 HTTP 请求头里显式设置 Content-Type 为 text/xml; charsetutf-8服务端接收时也强制用 UTF-8 解析。凡是涉及 SOAP 中文的场景第一排查方向就是编码。4. 最小可运行案例用 JAX-WS 发布一个 SOAP 服务4.1 开发环境与依赖说明这里我以 JDK 8 或 JDK 11 为例。JDK 8 自带 JAX-WS 的运行时和工具wsimport、wsgen直接可以用。JDK 11 之后 JAX-WS 从 JDK 中移除了你需要引入依赖以 Maven 项目为例dependency groupIdjakarta.xml.ws/groupId artifactIdjakarta.xml.ws-api/artifactId version3.0.1/version /dependency dependency groupIdcom.sun.xml.ws/groupId artifactIdjaxws-rt/artifactId version3.0.2/version /dependency如果是 JDK 8直接用 JDK 自带的 com.sun.xml.ws 包就行不需要额外依赖。不过考虑到版本演进我建议都用 Maven 显式引入依赖这样切 JDK 版本不用改代码。4.2 编写服务端接口和实现先定义一个接口。注意命名空间要提前规划好一般用公司的域名反写比如 com.example.greetingpackage com.example.greeting; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; WebService(targetNamespace http://example.com/greeting) public interface GreetingService { WebMethod String sayHello(WebParam(name name) String name); WebMethod OrderInfo getOrder(WebParam(name orderId) String orderId); }再写实现类。这里的重点是指定 endpointInterface并且实现接口的所有方法package com.example.greeting; import javax.jws.WebService; WebService( endpointInterface com.example.greeting.GreetingService, targetNamespace http://example.com/greeting, serviceName GreetingService ) public class GreetingServiceImpl implements GreetingService { Override public String sayHello(String name) { return Hello, name; } Override public OrderInfo getOrder(String orderId) { // 实际项目里这里会查数据库或者调用业务层 OrderInfo info new OrderInfo(); info.setOrderId(orderId); info.setStatus(PAID); return info; } }OrderInfo 是一个普通的 JavaBean一定要有无参构造函数属性要有 getter 和 setter否则 JAXB 序列化会报错。4.3 两种发布方式API 发布与部署到应用服务器JAX-WS 提供了一种极简的发布方式用 JDK 自带的 Endpoint 类package com.example.greeting; import javax.xml.ws.Endpoint; public class ServerBootstrap { public static void main(String[] args) { String address http://localhost:8080/greeting; Endpoint.publish(address, new GreetingServiceImpl()); System.out.println(Service published at address); } }运行这个 main 方法服务就在 http://localhost:8080/greeting 上线了。浏览器访问 http://localhost:8080/greeting?wsdl 就能看到自动生成的 WSDL 文档。这种方式适合本地调试、快速验证思路因为底层是 JDK 内置的 HTTP 服务器功能太单一不支持并发调优、安全管理这些生产级能力千万别把它用到生产环境。生产环境的正规姿势是打成 WAR 包部署到 Tomcat、WildFly、GlassFish 这类应用服务器上。用 WAR 包部署时你需要留意一件事应用服务器自带的 JAX-WS 实现和你的依赖是否冲突。比如 Tomcat 本身不带 JAX-WS那你把 jaxws-rt 打包进去没问题但 WildFly 自带 RESTEasy 和一套 JAX-WS 实现如果再塞一套 Metro可能出现类加载冲突报一些莫名其妙的 ClassNotFoundException。解决办法是打包时排除掉重复依赖或者用服务器提供的模块。4.4 客户端如何调用wsimport 生成代码服务端上线之后客户端调用有两种常见方式。一种是用 wsimport 工具根据 WSDL 生成客户端代理类另一种是用 Service 类动态调用。wsimport 是 JDK 自带的命令行工具用法很简单wsimport -keep -p com.example.client http://localhost:8080/greeting?wsdl指定 -keep 保存生成的 Java 源码-p 指定包名。生成完代码之后client 包里会有一个 GreetingService 类还有一个 GreetingServiceSoap 接口调用方式如下GreetingService service new GreetingService(); GreetingServiceSoap port service.getGreetingServiceSoap(); String result port.sayHello(Alice); System.out.println(result);这里有一个经验之谈wsimport 生成的代码里端口类型PortType接口和 Service 类之间的命名关系取决于 WSDL 里的定义。如果服务端的 targetNamespace 或者 serviceName 起得比较随意生成的类名会很难看。所以我一般建议在服务端 WebService 注解里显式设置 serviceName 和 portName让 WSDL 更规范客户端生成的代码也更可读。4.5 实操现场一次中文乱码的排查记录有一回对接一个老系统对方用 Java 写服务端我用 Java 写客户端联调的时候发现服务端返回的中文全部变成问号。翻日志看了半天服务端说收到的请求没问题我这边解析响应的字符串全是 ????。最后定位到问题出在 HTTP 请求头。我构造 SOAP 消息的时候用的是默认编码没有显式设置 Content-Type。服务端根据请求头里的编码声明解析 XML发现声明缺失或字符集不对就用了默认的 ISO-8859-1 去读中文自然全乱。解决方案就是在客户端发送请求之前设置请求属性((BindingProvider) port).getRequestContext().put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, http://localhost:8080/greeting ); ((BindingProvider) port).getRequestContext().put( MessageContext.HTTP_REQUEST_HEADERS, Collections.singletonMap(Content-Type, Collections.singletonList(text/xml; charsetutf-8)) );这个问题折磨了我快两个小时后来只要遇到 SOAP 接口中文乱码第一反应直接看两端的编码声明。如果你在项目里也遇到类似问题先别急着改代码逻辑抓包看看 HTTP 请求头和响应头的 Content-Type。5. JAX-RS 的实践姿态用 REST 风格构建 Web Service5.1 JAX-RS 核心注解速览与应用JAX-RS 这套规范用起来比 JAX-WS 轻很多。它不需要 WSDL也不需要生成客户端代理类直接通过 HTTP 动词和 URL 来表达语义。常用的注解就那几个Path标注在类或方法上定义资源路径GET、POST、PUT、DELETE标注 HTTP 方法Produces指定返回的媒体类型Consumes指定接收的媒体类型PathParam、QueryParam、HeaderParam从 URL 路径、查询参数、请求头中取值一个最简单的资源类长这样package com.example.rest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; Path(/greeting) public class GreetingResource { GET Path(/{name}) Produces(MediaType.APPLICATION_JSON) public Response sayHello(PathParam(name) String name) { String result {\message\:\Hello, name \}; return Response.ok(result).build(); } }部署到支持 JAX-RS 的服务器后访问 GET /greeting/Alice 就能拿到 JSON 格式的返回。这里我直接返回了一个手工拼接的 JSON 字符串只是为了演示。实际项目建议使用 Jackson 之类的库把 Java 对象序列化成 JSON避免手拼字符串出格式错误。5.2 JAX-RS 实现与运行时Jersey 和 RESTEasyJAX-RS 只是规范开发时还要选择一个实现。最常见的两个是 Jersey 和 RESTEasy。Jersey 是 SUN 主导的参考实现文档丰富社区活跃RESTEasy 是 JBoss 家族的产品和 WildFly 集成得很好。如果项目是 Spring Boot 技术栈更推荐直接用 Spring Web MVC因为 Spring MVC 本身就是一套 REST 风格的实现并不需要额外引入 JAX-RS。但如果你在纯 Jakarta EE 环境里开发选 Jersey 或 RESTEasy 都对关键看你的应用服务器是谁。如果用 WildFly优先 RESTEasy因为服务器原生集成省掉很多配置如果独立部署到 Tomcat用 Jersey 更省心。这里有个容易被忽略的点JAX-RS 的资源类默认是 Singleton 还是 Per-RequestJersey 默认是 Per-Request 的也就是每个请求都新建一个资源实例这样写起来不用太多考虑线程安全问题。RESTEasy 也类似。如果你在资源类里加了 Singleton 注解就要小心成员变量被多线程共享带来的并发问题。5.3 SOAP 到 REST 的迁移思路很多团队有历史包袱老系统全是 SOAP 接口新系统想用 REST就要做一个平滑的迁移方案。我的建议是不要一上来就重写而是先做协议适配层。适配层的职责是对外暴露 REST API对内调用 SOAP 服务。这样外部消费者完全感受不到后端的协议变化内部系统也可以逐步替换。适配层可以用一个 Java Web 应用承载REST 入口用 JAX-RS 或 Spring MVCSOAP 调用端用 JAX-WS 的客户端代理两边都是成熟技术风险可控。进一步如果后端服务本身已经支持 JAX-WS可以考虑直接升级到 JAX-RS 的同一个业务逻辑层。把 WebService 注解的方法搬到 JAX-RS 的资源类里逻辑层保持不变只改暴露层。这样做的好处是事务控制、业务校验都在 service 层不会因为换了协议而重复实现。6. 生产环境避坑指南事务、安全、日志与性能6.1 事务边界与线程安全Web Service 的方法本质上就是一个普通 Java 方法事务控制需要另做处理。如果你的服务部署在应用服务器上并且使用容器管理事务CMT可以通过在方法上加 Transactional 注解来声明事务边界。但有一个坑必须提醒SOAP 方法和 REST 资源方法都是在 Web 容器的线程里执行的不能自己 new 一个 Thread 去做异步操作然后指望事务还能覆盖到那个新线程。事务和线程绑定一旦跨线程事务就失效了。如果要异步处理要么用 JMS 消息队列要么用应用服务器提供的异步会话管理而不是直接 new Thread。另外JAX-WS 的实现类默认是 Singleton 的也就是说同一个实例可能被多个请求同时访问。如果你的实现类里放了一个可变的成员变量用来存请求数据就会出现线程安全问题。最好的做法是保持实现类无状态所有方法参数都从调用参数传递不要依赖类成员变量。6.2 安全从 WS-Security 到 HTTPS 与令牌Web Service 的安全问题常常被人忽视。尤其是内网服务很多人觉得反正外部访问不到就不做任何防护。但我见过太多次因为内网服务被滥用而导致的故障最小化的安全措施一定得有。传统的 SOAP 服务安全依赖 WS-Security它定义了一套在 SOAP Header 中携带安全令牌的机制。在 Java 里用 WSS4J 实现可以做到用户名密码校验、X.509 证书签名、消息加密。不过 WS-Security 的配置相当繁琐要处理密钥库、证书链、加密算法等一堆东西除非是监管要求或者极其敏感的金融场景一般项目不太会直接用。实际上最稳妥也最简单的做法是第一所有服务一律走 HTTPS禁止明文 HTTP 在公网传输。第二接口层面做认证鉴权REST 风格的可以用 JWT 或者 OAuth2SOAP 风格的可以在 SOAP Header 里放一个自定义的 token 字段服务端写 Handler 校验这个 token 是否有效。第三做好访问控制尽量通过防火墙或者网关限制调用方的 IP。这些方案实现成本低效果立竿见影。6.3 日志和报文记录排障的第一手材料服务端日志不能只打 INFO真正出问题的时候你需要的是报文级日志。我建议在服务端部署一个全局的 SOAP Handler 或者 JAX-RS 过滤器把所有请求和响应的报文记录下来。日志格式最好包含调用时间、客户端 IP、请求方法、请求路径、请求体、响应体、耗时。记录报文有两个副作用要注意。第一是敏感信息泄露如果报文里有密码、身份证号这类数据打明文日志就是安全隐患。处理办法是脱敏只记录部分字段或者统一用 [FILTERED] 代替。第二是性能问题生产环境流量大的时候每个报文都打全量日志磁盘会爆炸。我一般会做成按开关控制平时只记录请求方法、路径、响应码和耗时排查问题时再临时打开报文记录过一段时间就关掉。6.4 性能瓶颈与常见错误速查Web Service 的性能问题绝大多数集中在几个地方XML 序列化反序列化开销大、网络超时设置不合理、线程池被打满、下游依赖响应慢拖垮整个服务。XML 处理是 SOAP 服务最常见的性能瓶颈。尤其是使用 DOM 方式解析 XML会把整个 XML 树加载进内存报文一大会非常吃内存。正确做法是用 SAX 或 StAX 的流式解析方式或者尽量使用 JAXB 的 XmlType 直接绑定到 JavaBean避免手工处理 DOM。超时设置也是一门学问。很多系统的崩溃源于调用方的超时时间设置得比服务端的处理时间长导致请求堆在服务端线程池耗尽。经验值是外部服务调用超时一般设置 1 到 3 秒内部服务可以适当放宽到 5 秒但不建议超过 10 秒。超时一定要分级不能所有接口都用同一个时间。我整理了一份实际高频出现的问题速查表你在项目里可以直接参考现象可能原因排查方向服务一直报 404WAR 部署路径不对或资源类没被扫描到检查 web.xml 或应用服务器的 JAX-RS 配置中文乱码请求头或响应头缺少 charsetutf-8抓包看 Content-Type统一 UTF-8调用方报 UnknownHostException网络不通或 DNS 解析失败先 ping 服务端地址再查 hosts 配置返回 XML 反序列化失败服务端新增字段没有对应 setter确认 JavaBean 有没有无参构造和 getter/setter请求大量堆积、响应越来越慢下游依赖变慢超时设置过长调短超时做熔断降级SOAP 报文过大导致 OOM用 DOM 解析或没有限制报文大小改用 StAX/SAX限制最大请求大小方法调用到了但业务没生效事务没生效可能是方法内部自调用Spring 场景下避免同类内部调用要经代理调用结论最快的排障方式是先看报文和日志别急着看代码7. 关于学习路径的几句实在话Web Service 这类技术理论看起来特别多但实际工作中真正用得上的就是“先搞清楚选型逻辑、再看懂传输协议、然后会排查报文”这三板斧。我不建议一上来就啃 JAX-WS 规范原文那是给自己找罪受。先用最简单的方式把服务跑起来再逐步深入 Handler、安全、性能这些东西效率会高很多。还有一点要提醒Java EE 技术在往 Jakarta EE 演进包名从 javax.* 改成了 jakarta.*。如果你在切换版本的时候发现代码大面积报错不用慌大部分都是 import 路径的问题全局替换包名就能解决。但要注意有些第三方库对 Jakarta 命名空间的支持还不完善引入之前最好确认一下兼容性。我现在做新项目如果没有历史约束通常优先走 JAX-RS JSON 的路线保留 JAX-WS 仅用于对接遗留系统。这个策略帮我少踩了很多坑你在实际项目里也可以按这个思路评估。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →