Apache Tobago 5.15.2 快速入门与集成示例

从零手写最小 Tobago 5.15.2 工程跑通 JSF 全流程,覆盖 pom 依赖、faces-config/web.xml/tobago-config 三件套、构建部署、页面渲染与 managed bean 表单回显,以及 JDK17/JSTL/security-annotation 三大实测坑,全部命令与回显实测通过。

Apache TobagoApache Tobago2026/10/15

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-core 5.x 线最终版)
  • JSF 实现:Apache MyFaces 2.3.12(javax.faces JSF 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/faces 0 处,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 nullTobago 5 默认 security-annotation=disable,Tomcat 9 无 CDItobago-config.xml 配 ignore(见 2.4)
页面 500,日志 DatatypeConverter.encodeHexBinary NPEJDK 9+ 缺 JAXBpom 引入 jaxb-api:2.3.1(见 2.2)
启动失败,报找不到 javax.servlet.ServletContextListenerWAR 被部署到了 Tomcat 10.1改用 Tomcat 9
页面 500,日志 JspViewDeclarationLanguage 相关异常使用官方示例 WAR 时缺 JSTLWEB-INF/lib 放入 jstl-api 1.2.1 与 jstl-impl 1.2.5
编译报 release version 17 not supported编译机 JDK 低于 17升级 JDK 17
POST 后页面无变化、输入框清空Secret 或 ViewState 参数错误确认整页表单参数原样提交(浏览器不受影响)

六、进阶方向

跑通最小闭环后,可按需深入:

  1. 常用组件:下拉、日期选择、表格(tc:sheet)、弹窗等组件的属性与事件,见第 4 篇《Apache Tobago 5.15.2 常用组件与配置详解》。
  2. 主题机制:scarborough/speyside/standard 三主题关系、主题切换实测与多主题 jar 并存的排序行为,见第 5 篇《Apache Tobago 5.15.2 主题与进阶实战详解》。
  3. 运行时选型:JDK 8 环境选 Tobago 4.6.0(纯 Java 8 字节码,工程仅改版本号与 tobago-config 的 version 属性);Jakarta/Tomcat 10.1 环境等 Tobago 6.x(6.12.2,实测字节码为 jakarta.faces)。

七、参考链接