Apache Tobago 5.15.2 快速入门与集成示例
从零手写最小 Tobago 5.15.2 工程跑通 JSF 全流程,覆盖 pom 依赖、faces-config/web.xml/tobago-config 三件套、构建部署、页面渲染与 managed bean 表单回显,以及 JDK17/JSTL/security-annotation 三大实测坑,全部命令与回显实测通过。
Apache Tobago 5.15.2 快速入门与集成示例
目标:从空目录开始手写一个最小 Apache Tobago 5.15.2 工程,构建 WAR 并部署到 Tomcat 9,跑通「页面渲染 → 输入提交 → managed bean 处理 → 页面回显」的 JSF 完整生命周期;同时踩平 JDK 17、安全注解等三个必踩的坑。以下全部命令与回显均已在 Ubuntu 22.04(x86_64)+ OpenJDK 17.0.20.1 + Tomcat 9.0.123 实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04 LTS(x86_64)
- JDK:OpenJDK 17.0.20.1(Tobago 5.15.2 运行下限,实测 jar 内含 Java 17 字节码)
- 构建工具:Apache Maven 3.9.9
- 应用服务器:Apache Tomcat 9.0.123(javax 技术栈)
- Tobago 版本:5.15.2(
org.apache.myfaces.tobago:tobago-core5.x 线最终版) - JSF 实现:Apache MyFaces 2.3.12(
javax.facesJSF 2.3) - 实测工程目录:
/opt/tobago-demo
mvn -v | head -3
Apache Maven 3.9.9 (bb087f8bfad3c4b8e83de295bd7c42e57a957860)
Maven home: /opt/apache-maven-3.9.9
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64
⚠️ 技术栈定位(实测结论):Tobago 5.15.2 走
javax.faces(JSF 2.3)而非 Jakarta 命名空间——对官方 jar 反编译统计,javax/faces引用 351 处、jakarta/faces0 处,META-INF/faces-config.xml声明 2.3 规范。因此本篇配套 Tomcat 9 + MyFaces 2.3.12;Tobago 6.x(6.12.2)才是jakarta.faces,需 Tomcat 10.1 + MyFaces 4.x。同时 5.15.2 的 jar 中 889 个类里 112 个是 Java 17 字节码(major 61),JVM 必须是 JDK 17+。
二、10 分钟最小可用
从空目录到完整回显共 5 步:建目录(1 分钟)→ 写配置三件套与源码(3 分钟)→ 构建(1 分钟)→ 部署(2 分钟)→ 验证(3 分钟)。
2.1 创建工程目录
mkdir -p /opt/tobago-demo/src/main/java/demo
mkdir -p /opt/tobago-demo/src/main/webapp/WEB-INF
cd /opt/tobago-demo
2.2 pom.xml(全文照抄)
<?xml version="1.0" encoding="UTF-8"?>
<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>demo</groupId>
<artifactId>tobago-demo</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>11</maven.compiler.release>
<tobago.version>5.15.2</tobago.version>
<myfaces.version>2.3.12</myfaces.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.myfaces.tobago</groupId>
<artifactId>tobago-core</artifactId>
<version>${tobago.version}</version>
</dependency>
<dependency>
<groupId>org.apache.myfaces.tobago</groupId>
<artifactId>tobago-theme-scarborough</artifactId>
<version>${tobago.version}</version>
</dependency>
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-api</artifactId>
<version>${myfaces.version}</version>
</dependency>
<dependency>
<groupId>org.apache.myfaces.core</groupId>
<artifactId>myfaces-impl</artifactId>
<version>${myfaces.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>tobago-demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.4.0</version>
</plugin>
</plugins>
</build>
</project>
依赖职责速览:tobago-core 组件框架本体;tobago-theme-scarborough 默认主题(传递引入基础主题 tobago-theme-standard);myfaces-api/impl JSF 2.3 实现;jaxb-api 补 JDK 17 缺失的 JAXB(view-state 编码需要,缺了页面 writeState 抛 NPE);javax.servlet-api 由 Tomcat 提供,作用域必须是 provided。
2.3 web.xml(全文照抄)
src/main/webapp/WEB-INF/web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<display-name>tobago-demo</display-name>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Production</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.tobago.config.theme</param-name>
<param-value>scarborough</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>demo.xhtml</welcome-file>
</welcome-file-list>
</web-app>
要点:FacesServlet 是 JSF 入口,*.xhtml 映射让用户永远访问不到源码;org.apache.myfaces.tobago.config.theme 指定 Tobago 主题(本篇用 scarborough,主题机制详见第 5 篇)。
2.4 faces-config.xml 与 tobago-config.xml
src/main/webapp/WEB-INF/faces-config.xml(声明 managed bean,Tomcat 9 无 CDI,这是最省事的托管方式):
<?xml version="1.0" encoding="UTF-8"?>
<faces-config 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-facesconfig_2_3.xsd"
version="2.3">
<managed-bean>
<managed-bean-name>hello</managed-bean-name>
<managed-bean-class>demo.HelloBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
</faces-config>
src/main/webapp/WEB-INF/tobago-config.xml(关键:security-annotation 必须配 ignore):
<?xml version="1.0" encoding="UTF-8"?>
<tobago-config xmlns="http://myfaces.apache.org/tobago/tobago-config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://myfaces.apache.org/tobago/tobago-config http://myfaces.apache.org/tobago/tobago-config-5.1.xsd"
version="5.1">
<name>tobago-demo</name>
<security-annotation>ignore</security-annotation>
</tobago-config>
⚠️ 高频坑(实测复现):Tobago 5 默认
security-annotation为disable,渲染按钮时会走 CDI 授权检查(AuthorizationHelper是 CDI bean),而 Tomcat 9 没有 CDI 实现,页面直接 500,日志报NullPointerException: Cannot invoke "...AuthorizationHelper.isAllowed(...)" because "authorizationHelper" is null。配ignore关闭该检查。另一个坑:该元素在tobago-config-5.1.xsd的元素序列中排最后,若插在<name>之后会因 schema 校验失败导致整份配置被拒,恢复默认值。
2.5 托管 Bean 与页面
src/main/java/demo/HelloBean.java:
package demo;
import java.io.Serializable;
public class HelloBean implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String message;
public String submit() {
this.message = "你好, " + name + "! (来自 Tobago 回显)";
return null;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
src/main/webapp/demo.xhtml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:tc="http://myfaces.apache.org/tobago/component">
<f:view>
<tc:page id="page" label="Tobago Demo">
<tc:in id="name" label="姓名" value="#{hello.name}" required="true" requiredMessage="姓名不能为空"/>
<tc:button id="submit" label="提交" action="#{hello.submit}"/>
<tc:out id="echo" label="回显" value="#{hello.message}"/>
</tc:page>
</f:view>
</html>
页面语义:tc:page 是 Tobago 页面根组件(自动渲染消息区与状态隐藏域);tc:in 双向绑定输入框;tc:button 触发 #{hello.submit};tc:out 只读输出。组件用法详见第 4 篇。
三、构建与部署
3.1 构建 WAR
cd /opt/tobago-demo
mvn -B clean package 2>&1 | tail -6
[INFO] --- war:3.4.0:war (default-war) @ tobago-demo ---
[INFO] Packaging webapp
[INFO] Assembling webapp [tobago-demo] in [/opt/tobago-demo/target/tobago-demo]
[INFO] Processing war project
[INFO] Building war: /opt/tobago-demo/target/tobago-demo.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
3.2 依赖树确认(一次看清打包内容)
mvn -B dependency:tree 2>/dev/null | grep -E '^\[INFO\] [+\\|]' | head -10
[INFO] +- org.apache.myfaces.tobago:tobago-core:jar:5.15.2:compile
[INFO] | +- org.slf4j:slf4j-api:jar:2.0.17:compile
[INFO] | \- org.jsoup:jsoup:jar:1.20.1:compile
[INFO] +- org.apache.myfaces.tobago:tobago-theme-speyside:jar:5.15.2:compile
[INFO] | \- org.apache.myfaces.tobago:tobago-theme-standard:jar:5.15.2:compile
[INFO] +- org.apache.myfaces.tobago:tobago-theme-scarborough:jar:5.15.2:compile
[INFO] +- org.apache.myfaces.core:myfaces-api:jar:2.3.12:compile
[INFO] | \- commons-beanutils:commons-beanutils:jar:1.11.0:compile
[INFO] +- org.apache.myfaces.core:myfaces-impl:jar:2.3.12:compile
[INFO] \- javax.xml.bind:jaxb-api:jar:2.3.1:compile
主题包的传递关系清晰可见:scarborough 与 speyside 都向下依赖 standard(基础主题层)。
3.3 部署到 Tomcat 9
tar xzf apache-tomcat-9.0.123.tar.gz -C /opt 2>/dev/null
rm -rf /opt/apache-tomcat-9.0.123/webapps/*
cp /opt/tobago-demo/target/tobago-demo.war /opt/apache-tomcat-9.0.123/webapps/
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
cd /opt/apache-tomcat-9.0.123 && ./bin/startup.sh
Tomcat started.
四、验证
4.1 首页渲染
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/tobago-demo/
200
抓取页面关键结构(/></> 拆行便于阅读):
curl -s http://127.0.0.1:8080/tobago-demo/ | sed 's/></>\n</g' | grep -E "<title>|scarborough.*css|<tobago-page |tobago-in |type='text'|tobago-button|tobago-out|ViewState" | head -8
<title>Tobago Demo</title>
<link rel='stylesheet' href='/tobago-demo/tobago/scarborough/5.15.2/css/tobago.css' type='text/css'>
<tobago-page locale='en_US' class='container-fluid' id='page' focus-on-error='true' wait-overlay-delay-full='1000' wait-overlay-delay-ajax='1000'>
<tobago-in id='page:name' class='tobago-label-container tobago-auto-spacing tobago-required'>
<input type='text' name='page:name' id='page:name::field' class='form-control' required='required'>
<button type='button' id='page:submit' name='page:submit' class='tobago-button btn btn-secondary tobago-auto-spacing'>
<tobago-out id='page:echo' class='tobago-label-container tobago-auto-spacing tobago-auto-spacing'>
可以看到 Tobago 标签已按 scarborough 主题渲染为标准 HTML + 自定义元素:CSS 从主题资源库加载(/tobago-demo/tobago/scarborough/5.15.2/...),页面根是 <tobago-page>,隐藏域中带着 org.apache.myfaces.tobago.webapp.Secret(请求防伪令牌)与 javax.faces.ViewState(视图状态)。
4.2 表单回显(managed bean 交互)
浏览器验证:打开 http://<服务器IP>:8080/tobago-demo/,输入 q1w2e3r4,点「提交」,页面出现「你好, q1w2e3r4! (来自 Tobago 回显)」。
命令行等价验证(按 JSF Ajax 协议先 GET 取状态参数再 POST,javax.faces.partial.execute=@all 表示全树执行)实测:
VS=[QUMwRjlDMUJDOTlGNzlCQzAwMDAwMDAx] SEC=[HJb3rjEmN6zOVdQIclTmpw==]
POST HTTP 200
--- 回显检查 ---
你好, q1w2e3r4! (来自 Tobago 回显)
input type='text' name='page:name' id='page:name::field' value='q1w2e3r4' class='form-control' required='required'/
回显文本与输入框回填值同时出现,证明 JSF 六个阶段(恢复视图 → 应用请求值 → 处理校验 → 更新模型值 → 调用应用 → 渲染响应)完整执行。可在托管 bean 中加阶段监听器实测:
System.out.println("[PHASE-DONE] " + event.getPhaseId());
[PHASE-DONE] RESTORE_VIEW 1
[PHASE-DONE] APPLY_REQUEST_VALUES 2
[PHASE-DONE] PROCESS_VALIDATIONS 3
[PHASE-DONE] UPDATE_MODEL_VALUES 4
[PHASE-DONE] INVOKE_APPLICATION 5
[PHASE-DONE] RENDER_RESPONSE 6
[ACTION] submit() invoked, name=q1w2e3r4
💡 注意:手工模拟 POST 时三个参数缺一不可——
javax.faces.ViewState、org.apache.myfaces.tobago.webapp.Secret(Tobago 会话防伪令牌,服务端校验失败会静默跳过值应用阶段、直接重渲染空页面)与会话 Cookie。浏览器提交则无需关心这些细节。
五、常见报错速查
| 现象 | 根因 | 修复 |
|---|---|---|
页面 500,日志 authorizationHelper is null | Tobago 5 默认 security-annotation=disable,Tomcat 9 无 CDI | tobago-config.xml 配 ignore(见 2.4) |
页面 500,日志 DatatypeConverter.encodeHexBinary NPE | JDK 9+ 缺 JAXB | pom 引入 jaxb-api:2.3.1(见 2.2) |
启动失败,报找不到 javax.servlet.ServletContextListener | WAR 被部署到了 Tomcat 10.1 | 改用 Tomcat 9 |
页面 500,日志 JspViewDeclarationLanguage 相关异常 | 使用官方示例 WAR 时缺 JSTL | WEB-INF/lib 放入 jstl-api 1.2.1 与 jstl-impl 1.2.5 |
编译报 release version 17 not supported | 编译机 JDK 低于 17 | 升级 JDK 17 |
| POST 后页面无变化、输入框清空 | Secret 或 ViewState 参数错误 | 确认整页表单参数原样提交(浏览器不受影响) |
六、进阶方向
跑通最小闭环后,可按需深入:
- 常用组件:下拉、日期选择、表格(
tc:sheet)、弹窗等组件的属性与事件,见第 4 篇《Apache Tobago 5.15.2 常用组件与配置详解》。 - 主题机制:scarborough/speyside/standard 三主题关系、主题切换实测与多主题 jar 并存的排序行为,见第 5 篇《Apache Tobago 5.15.2 主题与进阶实战详解》。
- 运行时选型:JDK 8 环境选 Tobago 4.6.0(纯 Java 8 字节码,工程仅改版本号与 tobago-config 的 version 属性);Jakarta/Tomcat 10.1 环境等 Tobago 6.x(6.12.2,实测字节码为
jakarta.faces)。
七、参考链接
- Apache Tobago 官网:https://tobago.apache.org/
- Tobago Demo(官方在线演示):https://tobago-vm.apache.org/tobago-example-demo/
- Maven Central:https://repo1.maven.org/maven2/org/apache/myfaces/tobago/tobago-core/
- MyFaces Core 2.3 文档:https://myfaces.apache.org/core23/index.html
- JSF 2.3 规范:https://jakarta.ee/specifications/faces/2.3/
- Apache Tomcat 9 文档:https://tomcat.apache.org/tomcat-9.0-doc/