这份文档是对 `com.axis.innovators.box.browser` 包核心架构的深度解析。它涵盖了从底层核心代理到高层 UI 容器,以及 Java-JS 桥接机制的所有关键类。 --- # Axis Innovators Box Browser 核心类技术文档 本模块旨在为 Swing 应用程序提供一个高性能、易扩展的嵌入式浏览器解决方案。通过高度抽象的架构,开发者可以轻松地在 Java 窗口中嵌入 Web 页面,并实现双向通信。 --- ## 1. 核心架构设计 系统采用了 **代理模式 (Proxy Pattern)** 和 **构建者模式 (Builder Pattern)**: - **核心逻辑** (`BrowserCore`) 与 **窗口容器** (`BrowserWindow`/`BrowserWindowJDialog`) 分离。 - **配置逻辑** (`BaseBrowserBuilder`) 统一管理窗口属性。 - **通信逻辑** (`JsBridgeController`) 通过注解驱动。 --- ## 2. 接口与基类 (抽象层) ### 2.1 `BrowserContainer.java` (统一接口) 这是所有浏览器窗口容器的通用协议。无论是 `JFrame` 还是 `JDialog`,都必须实现此接口。 - **核心职责**:提供对外的统一 API,使得 `WindowRegistry` 和业务逻辑可以忽略窗口的具体物理类型。 - **关键方法**: - `executingJsCode(String script)`: 在 JS 环境中执行代码(支持跨线程)。 - `setController(JsBridgeController controller)`: 动态切换 Java 逻辑控制器。 - `getBrowser()` / `getMsgRouter()`: 获取底层 CEF 对象。 ### 2.2 `BaseBrowserBuilder.java` (通用配置器) 采用泛型链式调用的抽象构建器类。 - **核心职责**:存储所有窗口初始化所需的配置数据(尺寸、标题、初始 URL、图标等)。 - **主要字段**: - `htmlUrl` / `htmlPath`: 指定要加载的内容来源。 - `operationHandler`: 系统级消息处理器。 - `controller`: 初始的 JSBridge 控制器。 - `size`, `title`, `resizable`: 基础窗口属性。 --- ## 3. UI 容器层 (实现层) ### 3.1 `BrowserWindow.java` 继承自 `JFrame`,用于创建独立的主窗口或顶层窗口。 - **特点**:支持任务栏图标、最大化/最小化等标准窗口行为。 - **内部实现**:通过 `BrowserCore` 初始化浏览器组件,并将其作为 `BorderLayout.CENTER` 添加到内容面板。 ### 3.2 `BrowserWindowJDialog.java` 继承自 `JDialog`,用于创建子窗口或模态/非模态对话框。 - **特点**:支持设置 `parentFrame`,可以实现点击父窗口时弹窗保持在最前的逻辑。 - **应用场景**:设置界面、详情弹窗、登录界面等。 --- ## 4. 浏览器引擎核心 ### 4.1 `BrowserCore.java` (系统大脑) 这是最关键的类,封装了 JCEF 的所有复杂逻辑。 - **职责**: 1. **初始化**:管理 `CefClient` 的创建和 `CefBrowser` 的生命周期。 2. **Handler 路由**:自动设置加载监听、右键菜单处理、证书错误处理、文件选择对话框处理等。 3. **通信初始化**:配置 `CefMessageRouter` (基于 `window.javaQuery`)。 4. **脚本队列**:维护 `pendingScripts` 队列。如果页面尚未加载完成就调用了 JS 代码,代码会暂存在队列中,直到 `onLoadEnd` 触发后自动冲刷执行。 5. **资源注入**:自动向 Web 页面注入系统字体、主题颜色和 `extLibsPath` 等全局变量。 --- ## 5. Java-JS 桥接机制 (通信层) 这是实现“Java 调用 JS”和“JS 调用 Java”的核心组件。 ### 5.1 `JsMapping.java` (方法注解) 一个运行时注解,用于标记控制器中可以被 JS 访问的方法。 - **属性 `value()`**: - 若为空:默认挂载在 `tzd.方法名()`。 - 若为单纯字符串(如 `"login"`):挂载在 `tzd.login()`。 - 若包含点号(如 `"app.utils.calc"`):挂载在 `window.app.utils.calc()`。 ### 5.2 `JsBridgeController.java` (抽象控制器) 用户自定义业务逻辑的基类。 - **工作原理**: 1. **反射扫描**:在初始化时自动扫描带有 `@JsMapping` 的所有方法。 2. **代码生成**:`generateInjectionJs()` 会生成一套复杂的 JS 代理对象代码,在 `onLoadEnd` 时注入浏览器。 3. **参数转换**:利用 Gson 将 JS 传来的 JSON 数组自动反序列化为 Java 方法的参数类型(如 `String`, `int`, `boolean` 或自定义 POJO)。 4. **异步回调**:封装了 `window.javaQuery` 的复杂调用,使 JS 端可以像调用本地函数一样使用 `async/await` 获取结果。 --- ## 6. 开发流程示例 ### 第一步:定义控制器 ```java public class MyActionController extends JsBridgeController { @JsMapping("saveConfig") // JS 调用: tzd.saveConfig(data) public boolean save(String data) { System.out.println("Saving: " + data); return true; } @JsMapping("sys.getInfo") // JS 调用: sys.getInfo() public String getInfo() { return "Version 1.0.0"; } } ``` ### 第二步:启动窗口 ```java WindowRegistry.getInstance().createNewChildWindow("win01", builder -> { builder.title("我的应用") .htmlPath("index.html") .controller(new MyActionController()); // 绑定 }); ``` ### 第三步:前端调用 ```javascript // index.html async function doSave() { // 调用 Java 并获取返回值 const success = await tzd.saveConfig("{id: 1}"); if (success) { const info = await sys.getInfo(); alert("保存成功, " + info); } } ``` --- ## 7. 最佳实践建议 1. **资源管理**:始终通过 `WindowRegistry.unregisterWindow()` 关闭窗口,以确保内存和进程资源被释放。 2. **线程注意事项**: - `executingJsCode` 是线程安全的,可以在任意线程调用。 - 在 `@JsMapping` 标记的 Java 方法中,如果涉及 UI 更新(如修改 Swing 组件),必须包裹在 `SwingUtilities.invokeLater` 中。 3. **动态切换**:由于 `setController` 会重新注入脚本,建议在页面初次加载完成后进行控制器切换,以获得最佳稳定性。