1. 项目概述:为什么今天还要聊 JBoss(WildFly)?
如果你在某次系统迁移评审会上听到“老系统跑在 JBoss AS 7 上,升级得切到 WildFly”,或者在某家金融类企业的运维文档里看到“应用部署路径为/opt/wildfly/standalone/deployments/”,又或者在排查一个 Java EE 应用的类加载异常时,日志里反复出现org.jboss.modules.ModuleClassLoader—— 那么你正在直面一个被严重低估、却仍在关键生产环境中沉默运转的基础设施:JBoss(WildFly)应用服务器。
它不是云原生时代最耀眼的新星,也不是 Spring Boot 内嵌 Tomcat 那样轻量易上手的“默认选项”,但它却是过去十五年里,支撑大量政企核心业务系统(如某省社保结算平台、某大型能源集团设备监控中台、某银行信贷审批后台)稳定运行的底层脊梁。WildFly 不是“过时技术”,而是一种经过高强度、长周期、多版本演进验证的 Java EE / Jakarta EE 全栈能力交付载体——它把事务管理、JNDI 查找、JMS 消息、安全域配置、集群会话复制、模块化类加载这些企业级刚需,封装成一套可配置、可扩展、可审计的运行时契约。
我从 2012 年开始接触 JBoss AS 7,参与过三个跨年度的遗留系统现代化改造项目,其中两个最终选择保留 WildFly 作为主运行容器,而非全量迁移到 Spring Boot + Kubernetes。原因很实在:不是不想换,而是换不起——那些深度耦合了 JTA 分布式事务、EJB 远程调用、JCA 连接器、以及自定义 Security Realm 的老模块,重写成本远高于运维优化成本。WildFly 的价值,恰恰藏在这种“不声张但不可替代”的场景里:它不追求启动速度,但保证事务一致性;它配置略显繁复,但提供极细粒度的资源隔离与行为控制;它文档风格偏“手册感”,但每个 XML 配置项背后都有明确的规范依据和压测数据支撑。
这篇文章不讲“WildFly 是什么”,也不堆砌官网介绍。我要带你拆开它的外壳,看清楚:它到底在解决哪一类问题?哪些场景下它仍是不可替代的选择?它的配置逻辑如何自洽?当问题发生时,你该盯住哪几行日志、哪几个指标、哪几处配置?尤其是当你面对一份没有注释的standalone.xml、一个报错信息模糊的ClassNotFoundException、或是一个 CPU 持续 95% 却查不到热点线程的生产实例时,这篇内容就是你打开 WildFly 黑箱的第一把钥匙。
2. 核心设计哲学与架构选型逻辑
2.1 它不是 Tomcat,也不是 Spring Boot:理解 WildFly 的存在前提
很多初学者会困惑:“既然 Spring Boot 内嵌 Tomcat 能跑 WAR,为什么还要单独装 WildFly?”这个问题本身,就暴露了对 Java 企业级应用分层逻辑的误读。我们来做一个生活化类比:
把一个 Java Web 应用比作一栋住宅楼。
- Tomcat是“毛坯房+基础水电接口”:它提供 Servlet 容器、HTTP 接入、基础类加载,但你得自己拉线、装开关、接热水器——比如事务要自己配 JTA 实现(如 Atomikos),消息要自己集成 ActiveMQ 客户端,安全认证要自己写 Filter 或 Spring Security 配置。
- Spring Boot是“精装交付房+智能家居中控”:它把常见组件(数据库连接池、缓存、消息中间件客户端)都预装好、配好联动逻辑,你只需填入住需求(
application.yml),就能快速入住。但它默认不提供“整栋楼的消防系统审批资质”——即 Jakarta EE 规范的全栈合规性认证。- WildFly则是“带全套市政验收文件的标准化工业厂房”:它内置了通过 Jakarta EE TCK 认证的事务管理器(Narayana)、JMS 实现(ActiveMQ Artemis)、安全管理器(PicketBox / Elytron)、Web 服务引擎(RESTEasy)、甚至集群通信协议(JGroups)。你部署的 WAR/EAR,不是“跑起来就行”,而是承诺“按标准执行”——比如
@StatelessEJB 的事务边界、@Resource注入的数据源连接、<security-constraint>定义的 URL 权限,都会被 WildFly 的运行时严格校验与执行。
所以 WildFly 的核心定位,从来不是“更快更轻”,而是“更全更稳更可审计”。它的架构选型逻辑,围绕三个刚性需求展开:
规范契约优先:必须 100% 兼容 Jakarta EE(前身为 Java EE)规范。这意味着它的模块加载机制、JNDI 命名空间结构、JTA 事务传播规则,全部由 JSR 文档定义,而非框架作者的个人偏好。某高校实验室曾用 WildFly 8 验证一个分布式事务论文模型,正是因为其 Narayana 实现严格遵循 JTS 规范,实验结果具备学术可复现性。
运行时可治理性:企业级运维要求“看得清、控得住、改得准”。WildFly 提供 CLI(命令行接口)、Management Console(Web 控制台)、HTTP Management API 三套统一管理入口,所有操作(启停服务、修改线程池、更新数据源、部署应用)均生成可审计的操作日志,并支持回滚快照。这比手动改
server.xml+kill -9的 Tomcat 运维模式,高出至少两个成熟度等级。模块化隔离能力:WildFly 的
jboss-modules.jar是其灵魂所在。它抛弃了传统的双亲委派类加载模型,采用“模块依赖声明 + 显式导出/导入”的方式,让每个部署单元(WAR/EAR)拥有独立的类路径视图。这直接解决了“Log4j 和 Logback 版本冲突”、“不同应用共用同一份 Jackson 配置导致 JSON 序列化异常”等经典难题。我曾处理过一个案例:两个业务系统 A 和 B 同时部署在一台 WildFly 上,A 依赖旧版 Hibernate Validator 5.x(使用javax.validation.*包),B 使用 Jakarta EE 9+ 的 Validator 3.x(jakarta.validation.*包),WildFly 的模块系统天然隔离二者,零冲突。
提示:WildFly 的模块化不是“炫技”,而是为了解决真实痛点。当你看到
java.lang.ClassNotFoundException: org.jboss.resteasy.spi.ResteasyProviderFactory这类错误时,第一反应不该是“加依赖”,而应检查jboss-deployment-structure.xml中是否正确声明了resteasy-jaxrs模块依赖——这是 WildFly 的“游戏规则”。
2.2 WildFly 与 JBoss AS 的血缘关系:版本演进中的关键断点
很多人混淆 JBoss AS、WildFly、JBoss EAP,这里必须厘清:
JBoss AS(Application Server):是 Red Hat 开源社区版的应用服务器,最后稳定版本是AS 7.2.0.Final(2013 年发布)。它首次引入模块化架构(
jboss-modules)和统一管理模型(Unified Management Model),奠定了现代 WildFly 的基础。WildFly:是 JBoss AS 的直接继承者,于 2013 年底以WildFly 8名义重启开源项目。命名变更标志着架构彻底转向 Jakarta EE(当时还叫 Java EE)最新规范,并拥抱更激进的模块化与响应式设计。WildFly 10 是第一个完全支持 Java EE 7 Full Profile 的版本;WildFly 26 是首个全面拥抱 Jakarta EE 9(包名从
javax.*迁移至jakarta.*)的 LTS 版本;WildFly 30(2024 年发布)已原生支持 Jakarta EE 10 与 MicroProfile 6.0。JBoss EAP(Enterprise Application Platform):是 Red Hat 提供的商业支持版,基于 WildFly 某个稳定分支(如 EAP 7.x 基于 WildFly 10/11,EAP 8.x 基于 WildFly 26/27),增加企业级特性(如 SSO 集成、高级监控代理、FIPS 140-2 加密合规、长期安全补丁)。EAP ≠ WildFly 的“付费加强版”,而是 WildFly 的“企业加固发行版”——它禁用部分实验性功能,强化稳定性测试,并提供 SLA 保障。
为什么这个区分重要?因为你在生产环境遇到的问题,很可能与版本选择强相关。例如:
- 若你使用 WildFly 23 部署一个依赖
javax.xml.bind.JAXBContext的老应用,会直接抛NoClassDefFoundError,因为 JAXB 在 Java 11+ 中已被移除,而 WildFly 23 默认不包含java.xml.bind模块(需手动添加--add-modules=java.xml.bind启动参数); - 但若你用的是 EAP 7.4(基于 WildFly 18),Red Hat 已在该发行版中预置了兼容模块,无需额外配置。
注意:WildFly 官方只提供最新 3 个主版本的安全更新(如当前是 WildFly 30/29/28),旧版本漏洞需自行修复或升级。而 EAP 提供长达 10 年的生命周期支持(LTS),这是企业选型的关键决策点。
2.3 为什么 WildFly 仍活跃于关键系统?四个不可替代场景
尽管云原生浪潮席卷,WildFly 在以下四类场景中,依然保持着难以撼动的地位:
强事务一致性要求的金融/政务系统
某省级财政支付平台的核心账务模块,需确保“预算扣减 + 支付指令下发 + 会计凭证生成”三步操作原子性。它采用 WildFly 内置的 Narayana JTA 实现,配合 Oracle RAC 数据库的 XA DataSource,实测在 2000 TPS 下事务成功率 99.999%。若换成 Spring Boot + Atomikos,需自行处理 XA Recovery 日志清理、两阶段提交超时策略、以及与 Oracle RAC 的特定驱动兼容性问题,运维复杂度指数级上升。遗留 EJB 架构的平滑演进
某大型制造企业的 MES 系统,核心业务逻辑封装在无状态会话 Bean(Stateless Session Bean)中,已有 15 年代码资产。直接重写为 Spring Cloud 微服务,预估成本超 800 人日。团队选择 WildFly 26 + Jakarta EE 9 迁移路径:仅将javax.ejb.*替换为jakarta.ejb.*,调整ejb-jar.xml命名空间,其余代码零修改。迁移后性能提升 12%,且保留了 EJB 容器提供的自动线程池管理、远程调用负载均衡、以及方法级事务控制能力。高安全合规性审计场景
某金融机构的反洗钱(AML)分析引擎,需通过等保三级与 PCI DSS 认证。WildFly 的 Elytron 安全子系统提供 FIPS 140-2 认证的加密算法实现、基于角色的细粒度 Web 资源访问控制(RBAC)、以及完整的登录失败锁定与审计日志(含 IP、时间、用户名、操作类型)。这些能力在 Spring Security 中需大量定制开发才能达到同等审计要求。混合部署架构中的“粘合剂”角色
某电信运营商的 OSS 系统,前端为 Vue.js SPA,后端由 WildFly(承载传统 Java EE 服务)、Node.js(实时告警推送)、Python(AI 异常检测)三套运行时组成。WildFly 通过其内建的 Undertow Web 服务器,直接代理/api/v1/请求到 Java 服务,同时将/ws/路径转发给 Node.js WebSocket 服务。这种“一机多容器”的混合部署模式,在 WildFly 的undertow子系统中仅需 5 行 XML 配置即可完成,远比 Nginx 反向代理更轻量、更可控。
这些场景共同指向一个事实:WildFly 的价值,不在于它有多“新”,而在于它有多“稳”、多“全”、多“可预期”。当业务逻辑的复杂度远超基础设施的复杂度时,选择一个经过十年以上生产验证、规范完备、问题有迹可循的运行时,本身就是一种高效的技术决策。
3. 核心配置体系与实操要点解析
3.1 目录结构解剖:从bin/到standalone/deployments/的每一寸土地
WildFly 的目录结构设计极具工程理性,理解它,是避免“配置迷路”的第一步。以 WildFly 30 为例,解剖其核心目录:
wildfly-30.0.0.Final/ ├── bin/ # 启动与管理脚本 │ ├── standalone.sh # Linux 启动脚本(Windows 为 .bat) │ ├── jboss-cli.sh # CLI 管理工具入口 │ └── add-user.sh # 创建管理用户/应用用户 ├── docs/ # 文档(含 TCK 测试报告、规范兼容性说明) ├── modules/ # 所有模块定义(jar 包 + module.xml 描述文件) │ └── system/ # 系统模块(如 org.jboss.as.server, org.wildfly.extension.undertow) ├── standalone/ # 独立模式(Standalone)运行时根目录 │ ├── configuration/ # 核心配置文件 │ │ └── standalone.xml # 主配置文件(相当于 Tomcat 的 server.xml) │ ├── deployments/ # 应用部署目录(放 WAR/EAR 文件,自动热部署) │ ├── log/ # 日志文件(server.log, boot.log) │ ├── tmp/ # 临时工作目录(编译 JSP、解压 WAR) │ └── data/ # 运行时数据(H2 数据库文件、缓存数据) └── welcome-content/ # 默认欢迎页面(可替换为自定义首页)关键要点解析:
standalone.xml是心脏,但不是唯一:它定义了整个服务器的运行时行为(线程池、数据源、安全域、子系统配置)。但 WildFly 支持“配置覆盖”机制:你可以在standalone/configuration/下创建standalone-full.xml(启用全部子系统)或standalone-ha.xml(启用高可用集群),并通过启动参数-c standalone-ha.xml指定。不要试图在一个standalone.xml里塞满所有功能,而应按环境职责拆分配置文件。deployments/目录的“魔法”与陷阱:将myapp.war复制到此目录,WildFly 会自动解压并部署。但注意其背后的“部署标记机制”:WildFly 不是简单监听文件创建,而是检查同名.dodeploy标记文件。正确流程是:- 复制
myapp.war到deployments/ - WildFly 自动创建
myapp.war.dodeploy(触发部署) - 部署成功后,重命名为
myapp.war.deployed - 若部署失败,则变为
myapp.war.failed,并记录详细错误到server.log
实操心得:若你发现应用没部署,先检查
deployments/下是否存在.dodeploy文件;若想强制重新部署,删除.deployed文件并重建.dodeploy即可。永远不要直接在deployments/中编辑 WAR 内部文件——它会被下次部署覆盖。- 复制
modules/目录是 WildFly 的“操作系统内核”:每个模块(如org.hibernate:main)是一个独立目录,包含module.xml(声明依赖与导出包)和resources/(jar 包)。当你需要添加第三方库(如 Oracle JDBC 驱动),绝不能直接丢进lib/(WildFly 没有lib/目录!),而应:- 在
modules/下创建路径com/oracle/jdbc/main/ - 放入
ojdbc8.jar和module.xml(声明<module name="com.oracle.jdbc" />和<resource-root path="ojdbc8.jar"/>) - 在
standalone.xml的<datasource>中引用driver="oracle",并在<drivers>部分声明<driver name="oracle" module="com.oracle.jdbc"/>
这种“显式模块声明”机制,是 WildFly 解决类冲突的根本保障。
- 在
3.2standalone.xml配置精要:读懂 80% 的生产问题
standalone.xml是 WildFly 的“宪法”,但其 XML 结构庞大(默认超 2000 行)。我们聚焦最常修改、最易出错的五个核心区块:
3.2.1<management>:管理接口的“门禁系统”
<management> <security-realms> <security-realm name="ManagementRealm"> <authentication> <local default-user="$local" skip-group-loading="true"/> <properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/> </authentication> </security-realm> </security-realms> <management-interfaces> <http-interface security-realm="ManagementRealm"> <http-upgrade enabled="true"/> <socket-binding http="management-http"/> </http-interface> </management-interfaces> </management>mgmt-users.properties是管理用户凭据文件:add-user.sh脚本会自动在此文件中添加哈希密码。切勿手动编辑此文件的密码字段——哈希值需通过脚本生成,否则会导致登录失败。http-interface绑定的management-httpsocket:在<socket-binding-group>中定义,默认端口9990。若需外网访问,必须修改<interface name="management">的inet-address为0.0.0.0(默认是127.0.0.1),并确保防火墙放行。
提示:生产环境严禁开启
local认证(允许本地 Unix socket 登录),应强制使用properties或 LDAP 认证,并定期轮换管理员密码。
3.2.2<subsystem xmlns="urn:jboss:domain:undertow:14.0">:Web 服务器的“交通管制”
<subsystem xmlns="urn:jboss:domain:undertow:14.0"> <buffer-cache name="default"/> <server name="default-server"> <http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/> <https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/> <host name="default-host" alias="localhost"> <location name="/" handler="welcome-content"/> <filter-ref name="server-header"/> </host> </server> <servlet-container name="default"> <jsp-config/> <websockets/> </servlet-container> </subsystem>http-listener的redirect-socket="https":当 HTTP 请求到达时,自动 301 重定向到 HTTPS。但注意:此重定向发生在 Undertow 层,不经过应用代码,因此HttpServletRequest.getScheme()在应用中仍返回http。若应用需感知真实协议,应配置反向代理(如 Nginx)并设置X-Forwarded-Proto头,再在 WildFly 中启用proxy-address-forwarding="true"。servlet-container中的jsp-config:WildFly 30 默认禁用 JSP 编译(因 Jakarta EE 10 已移除 JSP 规范)。若必须运行老 JSP 页面,需显式启用,并注意 JSP 编译器(Eclipse JDT)的内存限制,否则大 JSP 文件会触发OutOfMemoryError。
3.2.3<subsystem xmlns="urn:jboss:domain:datasources:7.0">:数据源的“生命线”
<subsystem xmlns="urn:jboss:domain:datasources:7.0"> <datasources> <datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true"> <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url> <driver>h2</driver> <security> <user-name>sa</user-name> <password>sa</password> </security> </datasource> <drivers> <driver name="h2" module="com.h2database.h2"> <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class> </driver> </drivers> </datasources> </subsystem>use-java-context="true"是关键:它决定 JNDI 名称是否绑定到java:命名上下文(应用内可直接@Resource(lookup="java:jboss/datasources/ExampleDS"))。若设为false,则绑定到全局java:jboss/,需用完整 JNDI 名查找,极易出错。xa-datasource-classvsdriver-class:对于 Oracle/DB2 等支持 XA 的数据库,必须使用xa-datasource-class(如oracle.jdbc.xa.client.OracleXADataSource),否则无法参与 JTA 分布式事务。普通driver-class仅支持本地事务。
3.2.4<subsystem xmlns="urn:jboss:domain:logging:10.0">:日志的“黑匣子”
<subsystem xmlns="urn:jboss:domain:logging:10.0"> <console-handler name="CONSOLE"> <level name="INFO"/> <formatter> <named-formatter name="COLOR-PATTERN"/> </formatter> </console-handler> <periodic-rotating-file-handler name="FILE" autoflush="true"> <formatter> <named-formatter name="PATTERN"/> </formatter> <file relative-to="jboss.server.log.dir" path="server.log"/> <suffix value=".yyyy-MM-dd"/> <append value="true"/> </periodic-rotating-file-handler> <root-logger> <level name="INFO"/> <handlers> <handler name="CONSOLE"/> <handler name="FILE"/> </handlers> </root-logger> </subsystem>periodic-rotating-file-handler的suffix:.yyyy-MM-dd表示按天滚动,但 WildFly 30 默认使用SizeRotatingFileHandler(按大小滚动)。若需日期滚动,必须显式指定PeriodicSizeRotatingFileHandler并配置rotate-size和max-backup-index。root-logger的level是总开关:设为WARN,则所有INFO级别日志(包括应用log.info())均被屏蔽。调试时可临时改为DEBUG,但生产环境严禁TRACE级别——它会使日志量暴增 10 倍以上。
3.2.5<subsystem xmlns="urn:jboss:domain:elytron:15.0">:安全子系统的“新国标”
<subsystem xmlns="urn:jboss:domain:elytron:15.0"> <security-domains> <security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper"> <security-realm name="ApplicationRealm"/> </security-domain> </security-domains> <security-realms> <identity-realm name="ApplicationRealm" identity="path/to/application-users.properties"/> </security-realms> </subsystem>Elytron 取代了旧版 PicketBox:WildFly 11+ 默认启用 Elytron。
application-users.properties存储应用级用户(非管理用户),格式为username=HEX(sha256(password))。密码哈希需用elytron-tool.sh生成,而非add-user.sh。permission-mapper是权限映射核心:它将用户角色映射到具体操作权限。默认default-permission-mapper仅做简单角色匹配。若需精细控制(如“财务角色只能访问/api/v1/invoice”),需自定义constant-permission-mapper或regex-permission-mapper。
注意:修改
standalone.xml后,必须重启 WildFly 生效(CLI 的reload命令也可,但部分子系统修改仍需 full restart)。切勿在生产环境随意 reload,可能中断长连接。
3.3 CLI(命令行接口):比 Web Console 更强大的“手术刀”
WildFly CLI(jboss-cli.sh)是运维 WildFly 的终极武器。它比 Web Console 更精准、更可脚本化、更少出错。掌握以下高频命令,效率提升 50%:
3.3.1 连接与基础操作
# 连接到本地 WildFly(默认端口 9990) $ ./jboss-cli.sh --connect # 连接到远程 WildFly(指定 host:port) $ ./jboss-cli.sh --connect controller=192.168.1.100:9990 # 查看当前节点状态 [standalone@localhost:9990 /] :read-attribute(name=server-state) { "outcome" => "success", "result" => "running" } # 查看所有已部署应用 [standalone@localhost:9990 /] /deployment=:read-resource3.3.2 数据源动态配置(无需重启)
# 添加一个 MySQL 数据源(假设已安装 mysql module) [standalone@localhost:9990 /] /subsystem=datasources/jdbc-driver=mysql:add(driver-name=mysql,driver-module-name=com.mysql,driver-xa-datasource-class-name=com.mysql.cj.jdbc.MysqlXADataSource) # 创建数据源 [standalone@localhost:9990 /]># 查看 undertow 线程池状态 [standalone@localhost:9990 /] /subsystem=undertow/server=default-server/http-listener=default:read-resource(include-runtime=true) # 输出包含 active-count, max-active, queue-size 等实时指标 # 动态修改线程池最大线程数(立即生效) [standalone@localhost:9990 /] /subsystem=undertow/server=default-server/http-listener=default:write-attribute(name=max-connections,value=500) # 查看 JVM 启动参数 [standalone@localhost:9990 /] :read-attribute(name=java-vm-arguments) # 修改 JVM 堆内存(需 reload) [standalone@localhost:9990 /] /system-property=JAVA_OPTS:add(value="-Xms2g -Xmx2g -XX:+UseG1GC")3.3.4 批量脚本化部署(jboss-cli.sh --file=deploy.cli)
创建deploy.cli文件:
# 连接控制器 connect localhost:9990 # 部署应用 deploy /path/to/myapp.war --force # 设置 JVM 参数 /system-property=JAVA_OPTS:add(value="-Dmyapp.env=prod") # 重载服务器 :reload执行:
./jboss-cli.sh --file=deploy.cli提示:CLI 脚本是 CI/CD 流水线部署 WildFly 应用的标准方式,比手动复制 WAR 更可靠、更可追溯。
4. 实操全流程:从零部署一个 Jakarta EE 10 应用
4.1 环境准备:WildFly 30 + JDK 17 + Maven
步骤 1:下载与安装
- JDK 17(LTS):
https://adoptium.net/下载 Temurin 17 - WildFly 30:
https://www.wildfly.org/downloads/下载wildfly-30.0.0.Final.zip - 解压到
/opt/wildfly,设置环境变量:export JAVA_HOME=/opt/jdk-17 export WILDFLY_HOME=/opt/wildfly
步骤 2:启动与验证
# 启动 WildFly(前台运行,便于查看日志) $ $WILDFLY_HOME/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 # 验证:访问 http://localhost:9990 (Management Console) # 访问 http://localhost:8080 (Welcome Page)注意:
-b 0.0.0.0绑定所有网卡,-bmanagement 0.0.0.0允许远程管理。生产环境应限制为内网 IP。
4.2 开发一个 Jakarta EE 10 REST 服务
我们构建一个极简的BookService,演示 WildFly 原生能力:
pom.xml关键依赖:
<dependencies> <!-- Jakarta EE 10 API --> <dependency> <groupId>jakarta.platform</groupId> <artifactId>jakarta.jakartaee-api</artifactId> <version>10.0.0</version> <scope>provided</scope> </dependency> <!-- CDI 支持 --> <dependency> <groupId>jakarta.enterprise</groupId> <artifactId>jakarta.enterprise.cdi-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> </dependencies>BookResource.java(REST 端点):
@Path("/books") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class BookResource { @GET public List<Book> getAll() { return Arrays.asList( new Book(1L, "WildFly Deep Dive", "John Doe"), new Book(2L, "Jakarta EE 10 Cookbook", "Jane Smith") ); } @POST public Response create(Book book) { // 模拟保存 System.out.println("Creating book: " + book.getTitle()); return Response.status(CREATED).entity(book).build(); } }Book.java(JAX-RS Entity):
public class Book { private Long id; private String title; private String author; // 构造函数、getter、setter(省略) }web.xml(可选,WildFly 30 默认启用 Servlet 6.0):
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" version="6.0"> <display-name>BookService</display-name> </web-app>构建 WAR:
mvn clean package # 生成 target/bookservice.war4.3 部署与验证:五步走通
步骤 1:检查 WAR 结构
unzip -l target/bookservice.war | head -20 # 确认包含:WEB-INF/classes/, WEB-INF/web.xml, META-INF/MANIFEST.MF步骤 2:部署到 WildFly
# 方法一:复制到 deployments/ cp target/bookservice.war $WILDFLY_HOME/standalone/deployments/ # 方法二:使用 CLI(推荐,可获反馈) $ $WILDFLY_HOME/bin/jboss-cli.sh --connect [standalone@localhost:9990 /] deploy /path/to/bookservice.war步骤 3:验证部署状态
- 查看
server.log:INFO [org.jboss.as.server] (management-handler-thread - 2) WFLYSRV0010: Deployed "bookservice.war" (runtime-name : "bookservice.war") - CLI 查询:
[standalone@localhost:9990 /] /deployment=bookservice.war:read-resource {"outcome" => "success", "result" => {"enabled" => true, "status" => "OK"}}
步骤 4:调用 REST API
# 获取所有图书 curl -v http://localhost:8080/bookservice/rest/books # 创建新图书