从 JavaFX WebView 迁移至 JxBrowser

长久以来,JavaFX 一直包含一个内置的 WebView 组件,这是在 Java 应用中渲染 Web 内容的一个稳定方案。然而,在更复杂或要求更高的使用场景中,它可能就不够用了。因此,许多开发者转向了像 JxBrowser 这样的替代方案。

在本迁移指南中,我们将详细介绍如何从 JavaFX WebView 迁移至 JxBrowser,并提供代码示例以及相关 JxBrowser 文档的链接。

本文侧重介绍如何迁移至 JxBrowser。若想了解为何要迁移,请参阅文章 JxBrowser 还是 JavaFX WebView,我们在其中详细解析了这两种方案的技术和架构差异。

依赖项

将 JxBrowser 添加到项目中,就像将一些 JAR 文件添加到类路径中一样简单。例如,一个运行于 Windows 的 JavaFX 应用将需要以下文件:

  • jxbrowser-8.9.2.jar. 该文件包含 JxBrowser 的大部分 API。
  • jxbrowser-javafx-8.9.2.jar. 该文件包含 JxBrowser 的 JavaFX 组件。
  • jxbrowser-win64-8.9.2.jar. 该文件包含适用于 64 位 Windows 的 Chromium 二进制文件。

你可以从 JxBrowser 8.9.2 版本发布说明页面下载所需文件。

如果您使用的是标准的 Maven 或 Gradle,只需像平常一样添加 Maven 仓库中的依赖项即可:

Maven

<repositories><repository><id>com.teamdev</id><url>https://europe-maven.pkg.dev/jxbrowser/releases</url></repository>
</repositories>
<dependency><groupId>com.teamdev.jxbrowser</groupId><artifactId>jxbrowser-javafx</artifactId><version>{version}</version>
</dependency>
<dependency><groupId>com.teamdev.jxbrowser</groupId><artifactId>jxbrowser-win64</artifactId><version>{version}</version>
</dependency>

Gradle

plugins {id("com.teamdev.jxbrowser") version "{gradle_plugin_version}"
}
jxbrowser {version = "{version}"
}
dependencies {implementation(jxbrowser.javafx)implementation(jxbrowser.win64)
}

线程安全性

WebViewWebEngine 并非线程安全的;访问它们及其 DOM/JavaScript 对象时,必须始终仅从 JavaFX 应用程序线程进行。

而 JxBrowser 是线程安全的。您可以在不同线程中安全地使用 JxBrowser 对象。不过,在 JavaFX 应用线程中调用 JxBrowser API 时需谨慎,因为它的许多方法是阻塞式的。为了避免影响用户体验,我们通常建议不要在 JavaFX 应用线程中调用 JxBrowser。

迁移

创建浏览器

JavaFX 提供了可视化的 WebView 组件(可添加到场景中),以及非可视的 WebEngine(包含实际的 Browser API)。

创建和使用方法如下:

WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
webEngine.load("https://example.com");
scene.getRoot().getChildren().add(webView);

JxBrowser 同样由可视化和非可视化部分组成。该库提供了非可视化的 EngineBrowser 对象,它们封装了 Browser API;还提供了一个可视化的 BrowserView 组件,可将其添加到场景中以显示加载的 Web 内容。

// 非可视化部分:
Engine engine = Engine.newInstance(HARDWARE_ACCELERATED);
Browser browser = engine.newBrowser();
browser.navigation().loadUrl("https://example.com");// 可视化部分:
Platform.runLater(() -> {BrowserView browserView = BrowserView.newInstance(browser);  scene.getRoot().getChildren().add(browserView);
});

在上述示例中,我们创建了一个非可视化的 Engine 实例,用于表示 Chromium 主进程。然后,我们创建一个非可视化的 Browser 实体,用于表示主进程中的特定浏览器——类似于 Google Chrome 中的浏览器标签页。最后,我们创建一个可视化的 BrowserView 节点并将其添加到场景中。

提示: 就像在 Google Chrome 中可以打开多个标签页一样,您可以在同一个 Engine 实例中创建多个 Browser 对象。

如需了解 JxBrowser API 中主要组件、进程模型及其他架构细节,请查阅架构指南。

关闭浏览器

在 JavaFX 中,并不需要显式关闭 WebView 实例。通常将其从场景图(scene graph)中移除就已足够。

而在 JxBrowser 中,仅从场景图中移除 BrowserView 不会关闭浏览器并释放所有已分配的资源。你必须手动关闭 BrowserEngine 对象:

JavaFX

scene.getRoot().getChildren().add(webView);

JxBrowser

// 关闭单个 Browser。
browser.close();// 关闭 Engine。此操作将自动关闭其包含的所有 Browser。
engine.close();

页面导航

WebEngine 中的导航功能几乎可以直接转换为 JxBrowser 的调用方式:

JavaFX

webEngine.load("https://example.com");
webEngine.reload();WebHistory history = webEngine.getHistory();
var currentIndex = history.getCurrentIndex();
var historySize = history.getEntries().size();// 后退到上一个历史页面。
var previousPage = currentIndex - 1;
if (previousPage >= 0 && previousPage < historySize) {history.go(previousPage);
}// 前进到下一个历史页面。
var nextPage = currentIndex + 1;
if (nextPage < historySize) {history.go(nextPage);
}

JxBrowser

Navigation navigation = browser.navigation();
navigation.loadUrl("https://example.com");
navigation.reload();navigation.goBack();
navigation.goForward();// 跳转到指定的历史记录索引
navigation.goToIndex(2);
导航监听器

在这两种解决方案中,加载过程都是在后台进行的,因此需要注册监听器来检测加载何时完成。

在 JavaFX 中,可以通过监听加载工作器的状态来实现:

var worker = webEngine.getLoadWorker();
worker.stateProperty().addListener((ov, oldState, newState) -> {if (newState == State.SUCCEEDED) {// 此处可以执行 JavaScript 并访问 DOM 树。} else {System.out.println("导航失败!");}
});

在 JxBrowser 中,通知更加精细:

// 当导航操作完成时会触发该事件。
// 此时 frame 和 DOM 树可能尚未初始化。
navigation.on(NavigationFinished.class, event -> {if (event.error() != OK) {System.out.println("Navigation failed!");}
});// 当 frame 的文档加载完成,且可以访问 DOM 时,会触发此事件。
navigation.on(FrameDocumentLoadFinished.class, event -> {// 此处可以执行 JavaScript 并访问 DOM 树。
});// 此回调允许您在 frame 刚刚完成加载、但**尚未执行其自身的 JavaScript**之前
// 执行您的 JavaScript 代码。
browser.set(InjectJsCallback.class, params -> {Frame frame = params.frame();JsObject window = frame.executeJavaScript("window");if (window != null) {...}return Response.proceed();
});

JxBrowser 共提供种细粒度的导航事件。完整列表请参阅导航事件文档。

从 Java 调用 JavaScript

在这两种方案中,您都可以执行任意的 JavaScript 代码,在 Java 中获取 JavaScript 对象,并享受自动类型转换的便利:

JavaFX

// JavaScript 对象会被转换为 JSObject。
JSObject dialogs = (JSObject) webEngine.executeScript("dialogs");
dialogs.call("showError", "The card number is not correct!");// JavaScript 字符串会被转换为 String。
String locale = (String) dialogs.getMember("locale");

JxBrowser

browser.mainFrame().ifPresent(frame -> {// JavaScript 对象会被转换为 JsObject。JsObject dialogs = frame.executeJavaScript("dialogs");jsObject.call("showError", "The card number is not correct!");// JavaScript 字符串会被转换为 String。String locale = dialogs.property("locale");
});

JavaFX 会自动转换传入的 JavaScript 值。原始类型会被转换为对应的 Java 类型,JavaScript 对象则会被转换为 JSObject 实例。

JxBrowser 执行类似的转换,但为特定的 JavaScript 类型(例如函数、PromiseArrayBuffer 等)提供了专用的 Java 类型。在类型转换指南中可查看完整列表。

对于用于访问带索引的 JavaScript 对象的 JSObject.getSlot()JSObject.setSlot() 方法,JxBrowser 没有直接的替代方案。

JavaScript 对象的生命周期

在 JavaFX 和 JxBrowser 中,只要对应的 JavaScript 对象还存在,JSObjectJsObject 实例就能正常工作。当 JavaScript 对象被垃圾回收或所在的 frame 加载了新的文档时,该对象就会失效。无论是在 JavaFX 还是 JxBrowser 中,尝试使用已失效的 JavaScript 对象都会抛出异常。

JavaScript 对象被垃圾回收的时间难以预测,因此在 JxBrowser 中,传递给 Java 的 JavaScript 对象会被保护,防止被垃圾回收,直到新文档加载时才会关闭。若想释放对该 JavaScript 对象的引用,使其可以被垃圾回收,可以调用 JsObject.close() 方法:

JsObject persistentObject = frame.executeJavaScript("dialogs");
persistentObject.close();

从 JavaScript 调用 Java

要从 JavaScript 调用 Java 代码,需要将 Java 对象注入到 JavaScript 环境中。这两种方案中的做法非常相似:

JavaFX

public static class GreetingService {public void greet(String name) {System.out.println("Hello, " + name + "!");}
}...GreetingService greetings = new GreetingService();
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("greetings", greetings);

JxBrowser

@JsAccessible
public static class GreetingService {public void greet(String name) {System.out.println("Hello, " + name + "!");}
}...GreetingService greetings = new GreetingService();
JsObject window = frame.executeScript("window");
window.putProperty("greetings", greetings);
成员访问权限

在 JavaFX 中,JavaScript 可以访问被注入的 Java 对象的所有公共成员。

而在 JxBrowser 中,需要显式地将 Java 类及其成员标记为允许被 JavaScript 访问:

// 类的所有公共成员都将可被访问。
@JsAccessible
public class AccessibleClass {public String sayHelloTo(String firstName) {...}
}// 仅该类的单个方法可被访问。
public class RestrictedClass {@JsAccessiblepublic String sayHelloTo(String firstName) {...}
}

对于无法添加注解的类(例如标准库类),可以使用以下方式使其可访问:

JsAccessibleTypes.makeAccessible(java.util.HashMap.class);

更多关于如何使对象对 JavaScript 可访问的信息,请参阅 JavaScript 指南。

Java 对象的生命周期

JavaFX 对传递给 JavaScript 的 Java 对象使用弱引用。这意味着如果该对象被垃圾回收,其对应的 JavaScript 对象会变为 undefined

而 JxBrowser 使用的是强引用,会防止 Java 对象被回收。只有在以下几种情况下,引用才会被移除:frame 加载了新文档;frame 被移除;或 browser 被关闭时。

代理配置

JavaFX 的 WebView 使用的是 Java 运行时自带的网络栈,因此会自动遵循 Java 的代理配置。

在 JxBrowser 中,Chromium 在单独的进程中使用其自身的网络,并遵循系统代理设置。如果您不想使用系统设置,可以为每个 Profile 单独配置代理:

JavaFX

// 配置代理设置。
System.setProperty("http.proxyHost", "proxy.com");
System.setProperty("http.proxyPort", "8080");
System.setProperty("https.proxyHost", "proxy.com");
System.setProperty("https.proxyPort", "8081");
System.setProperty("nonProxyHosts", "example.com|microsoft.com");// 配置代理身份验证。
Authenticator.setDefault(new Authenticator() {protected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication("username", "password".toCharArray());}
});

JxBrowser

// 配置代理设置。
var profile = engine.profiles().defaultProfile();
var exceptions = "example.com,microsoft.com";
var proxyRules = "http=proxy.com:8080;https=proxy.com:8081";
profile.proxy().config(CustomProxyConfig.newInstance(proxyRules, exceptions));// 配置代理身份验证。
profile.network().set(AuthenticateCallback.class, (params, tell) -> {if (params.isProxy()) {tell.authenticate("username", "password");} else {// 跳过其他身份验证请求。tell.cancel();}
});

DOM 访问

JavaFX 和 JxBrowser 提供了一组类似的功能来访问 DOM 树:

JavaFX

var document = webEngine.getDocument();
var element = document.getElementById("exit-app");
((EventTarget) element).addEventListener("click", listener, false);

JxBrowser

browser.mainFrame().flatMap(Frame::document).flatMap(document -> document.findElementById("exit-app")).ifPresent(element -> {element.addEventListener(CLICK, listener, true);});

在 JxBrowser 中,DOM 节点在 Java 和 JavaScript 之间传递时会自动转换为对应的 Java 类型。与 JsObject 类似,它们在浏览器中会被保护,不会被垃圾回收。如需手动释放资源,可调用 close() 方法,使其可被回收。

更多关于 DOM 操作的内容,请参阅 DOM 指南。

打印功能

JavaFX 提供了 API 来打印任何可视节点,包括 WebView。您可以选择打印机、通过代码配置部分打印参数,或者向用户展示系统打印对话框。

JxBrowser 则使用 Chromium 的打印能力。它同样支持选择打印机、以编程方式设置打印参数,并在需要时显示 Chromium 的打印预览对话框。

JavaFX

var printer = findMyPrinter(Printer.getAllPrinters());
var job = PrinterJob.createPrinterJob(printer);
if (showDialogs) {// 向用户显示系统对话框。job.showPageSetupDialog(stage);job.showPrintDialog(stage);
} else {// 或者静默打印var settings = printerJob.getJobSettings();settings.setCopies(3);settings.setCollation(COLLATED);webView.getEngine().print(job);printerJob.job();
}

JxBrowser

browser.set(PrintCallback.class, (params, tell) -> {if (showDialogs) {// 向用户显示系统对话框。tell.showPrintPreview();} else {// 或静默打印。tell.print();}
});// 注册 `PrintHtmlCallback` 用于打印 HTML 页面。
// 若从 PDF 文件发起打印,则需使用 `PrintPdfCallback`。
browser.set(PrintHtmlCallback.class, (params, tell) -> {var printer = findMyPrinter(params.printers());var job = printer.printJob();var settings = job.settings();settings.copies(3);settings.enableCollatePrinting();job.on(PrintCompleted.class, event -> {System.out.println("Printing completed");});tell.proceed(printer);
});browser.set(PrintPdfCallback.class, (params, tell) -> {...
});

提示: 即使没有系统打印机,您也可以使用 Chromium 内置的 PDF 打印机:params.printers().pdfPrinter()

有关如何配置打印功能的更多信息,请参阅打印指南。

JxBrowser 中的打印预览对话框

用户代理

在 JavaFX 中,您可以自定义 Browser 的用户代理(User-Agent)。

在 JxBrowser 中,您可以自定义单个 Browser 的用户代理,也可以自定义整个 Engine:

JavaFX

webEngine.setUserAgent("custom user agent");

JxBrowser

// 在 Engine 启动时配置全局用户代理。
var opts = EngineOptions.newBuilder(HARDWARE_ACCELERATED).userAgent("custom user agent").build();
var engine = Engine.newInstance(opts);// 或者,为特定 Browser 配置 UI。
browser.userAgent("custom user agent");

用户数据目录

在 JavaFX 中,用户数据目录用于存储本地存储中的数据。您可以显式配置该目录,或者 Engine 会根据操作系统和用户偏好自动选择。

在 JxBrowser 中,用户数据目录存储所有用户数据,包括缓存、本地存储和其他相关信息。您可以在启动 Engine 时配置该目录,或者 JxBrowser 会使用临时目录,该目录将在 Engine 关闭时被删除:

JavaFX

webEngine.setUserDataDirectory(new File("/path/to/directory"));

JxBrowser

var opts = EngineOptions.newBuilder(HARDWARE_ACCELERATED).userDataDir(Paths.get("/path/to/directory")).build();
var engine = Engine.newInstance(opts);

请注意,同一个用户数据目录不能被单个或不同 Java 应用中运行的多个 Engine 实例同时使用。尝试使用同一个用户数据目录将导致 Engine 创建过程中抛出异常。

弹出窗口

在 JavaFX 中,当网页想要在新窗口中打开内容时,WebEngine 不会创建新窗口。相反,它会用新窗口替换当前加载的页面。

通过注册自定义弹出处理器,可以更改此行为:

webEngine.setCreatePopupHandler(features -> {if (noPopups) {// 返回 null 会取消弹出窗口的创建。return null;}// 通过创建新的 WebView,可以指示 JavaFX 为新弹出窗口使用它。var popupView = new WebView();scene.getRoot().getChildren().add(popupView);return popupView.getEngine();
});

在 JxBrowser 中,所有弹出窗口默认都是被抑制的。要更改此设置,需注册 CreatePopupCallback

browser.set(CreatePopupCallback.class, params -> {return noPopups? CreatePopupCallback.Response.suppress(): CreatePopupCallback.Response.create();}
});

如果允许创建弹出窗口且 BrowserView 在 UI 中可见,JxBrowser 会在新的 Stage 中打开弹出窗口。你可以在 OpenBrowserPopupCallback 中自定义此行为:

browser.set(OpenBrowserPopupCallback.class, params -> {var popupBrowser = params.popupBrowser();var popupBounds = params.initialBounds();Platform.runLater(() -> {var popupView = BrowserView.newInstance(browser);scene.getRoot().getChildren().add(popupView);});return OpenBrowserPopupCallback.Response.proceed();
});

有关处理弹出对话框的更多信息,请参阅弹出窗口指南。

JavaScript 对话框

JavaFX 和 JxBrowser 都允许您自定义 JavaScript 对话框(如 confirm、prompt 和 alert)的行为:

JavaFX

webEngine.setConfirmHandler(value -> {if (silent) {return null;} else {return showMyConfirmDialog();}
});webEngine.setPromptHandler(promptData -> {if (silent) {return null;} else {return showMyPromptDialog(promptData);}
});webEngine.setOnAlert(event -> System.out.println("Alert happened!"));

JxBrowser

browser.set(ConfirmCallback.class, (params, action) -> {if (silent) {action.cancel();} else {var result = showMyConfirmDialog(params);if (result) {action.ok();} else {action.cancel();   } }
});browser.set(PromptCallback.class, (params, action) -> {if (silent) {action.cancel();} else {action.ok(showMyPromptDialog(params));}
});browser.set(AlertCallback.class, (params, action) -> {System.out.println("Alert happened");action.ok();
});

如果您不配置这些处理程序,JavaFX 默认会抑制对话框 —— confirm 对话框返回 false,prompt 对话框返回空字符串。而 JxBrowser 则会调用 JavaFX 的默认对话框实现来显示这些对话框。

您可以阅读对话框指南,了解如何自定义文件选择器、身份验证和其他类型的对话框。

自定义 CSS

在 JavaFX 中,可以通过设置样式表文件路径,或使用包含样式的 Data URL 来注入自定义 CSS。

在 JxBrowser 中,可以通过将 CSS 样式作为字符串传入来注入自定义样式:

JavaFX

webEngine.setUserStyleSheetLocation("file:///path/theme.css");

JxBrowser

// 此回调在文档准备就绪后触发,此时可注入 CSS。
browser.set(InjectCssCallback.class, params -> {var styles = readFile("file:///path/theme.css")return InjectCssCallback.Response.inject(styles);
});

总结

JavaFX WebView 和 JxBrowser 都提供了类似的功能,从 WebView 迁移至 JxBrowser 不会给您带来太多麻烦。

在本指南中,我们提供了迁移 WebView 大部分功能的代码示例,并附上了相关文档的链接。

尽管实际项目中的迁移工作可能会比较复杂,但我们相信通过本指南可以大大简化这一过程。您可以将其作为迁移项目的起点,如有疑问,欢迎随时联系我们。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914056.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914056.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将 Go 应用从 x86 平台迁移至 Amazon Graviton:场景剖析与最佳实践

简介 近年来&#xff0c;Amazon Graviton 处理器以其优越的性价比和强劲的性能&#xff0c;成为了构建高效、可扩展云原生应用的重要选择。Graviton 采用基于 Arm64 架构的芯片&#xff0c;与传统的 x86 架构相比存在不少架构差异。虽然 Go 天生对 Arm64 具有良好支持&#xf…

arcgis api for js 设置地图服务请求带有请求头信息

通过地图的config模块的请求拦截器来设置请求头信息&#xff0c;如下示例&#xff1a; 1、引入&#xff1a;‘esri/config’ 1、设置请求头信息 import { loadArcgisModules } from /utils/map/mapLoadUtil export default { mounted() {this.loadMap()}, methods: {/** ****…

工业通信升级新选择:耐达讯CCLINKIE转Modbus TCP网关

在工业自动化系统中&#xff0c;协议转换网关的选择直接影响系统稳定性与通信效率。对于CCLINKIE转Modbus TCP场景&#xff0c;耐达讯通信技术网关凭借以下特性&#xff0c;成为多个项目中的优选方案。技术选型要点协议兼容性支持CCLINKIE的令牌环机制与Modbus TCP的TCP/IP协议…

使用python的 FastApi框架开发图书管理系统-前后端分离项目分享

今天给大家分享一个 我最近使用python 框架 fastapi 写的一个web项目 &#xff0c;叫图书管理系统。项目主要是来巩固 python的编程技术。使用的是前端后 分离开发。 主要实现的功能&#xff1a; 1、用户管理&#xff1a;可以新增、编辑、删除用户信息。 2、图书管理&#xff1…

上位机知识篇---Docker

Docker 详细介绍 一、Docker 是什么 Docker 是一个开源的容器化平台&#xff0c;它允许开发者将应用程序及其依赖项打包到一个标准化的单元&#xff08;称为容器&#xff09;中&#xff0c;确保应用在任何环境中都能以相同的方式运行。 简单来说&#xff0c;Docker 解决了 &…

蓝桥杯第十六届(2025)真题深度解析:思路复盘与代码实战

> 省一选手的血泪经验:**避免这些坑,你也能冲进国赛!** 2025年蓝桥杯省赛已落下帷幕,作为近年来**难度最高的一届竞赛**,不少选手在考场上遭遇了“滑铁卢”。本文将以C++ B组真题为例,逐题解析解题思路,并提供**优化后的AC代码与详细注释**。笔者最终排名省一前40%,…

使用gdal读取shp及filegdb文件

一、使用qgis开源工具构建两个文件&#xff0c;分别是filegdb和shp&#xff0c;每个文件包含一个图层&#xff0c;图层内容只包含一个字段&#xff1a;id&#xff0c;有两个数据行&#xff0c;图层几何为多边形&#xff0c;图层都是如下的效果。二、使用rust读取上述文件 rust依…

从0开始学习R语言--Day44--LR检验

之前我们提到用LM检验的方式&#xff0c;来判断数据在空间上是否受到邻近数据及其残差的影响&#xff0c;但是LM检验是采用直接计算的方式&#xff0c;只关注了数据的残差平方和&#xff0c;没有数据关于依赖项的考虑&#xff0c;容易被结果误导。而LR检验虽然在结果上有时候跟…

openEuler 24.03 (LTS-SP1) 下私有镜像仓库部署与自签 SSL 全流程目标

目录 openEuler 24.03 (LTS-SP1) 下私有镜像仓库部署与自签 SSL 全流程 1 创建根 CA 与服务器证书&#xff08;修正版&#xff1a;SAN 写法兼容所有 OpenSSL&#xff09; 2 配置 Docker Compose 文件 3 客户端节点信任 CA 3.1 Docker 3.2 containerd 4 推送 / 拉取测试 …

mysql的LIMIT 用法

常见用法1. 限制返回行数-- 返回前5条记录 SELECT * FROM products LIMIT 5;2. 分页查询&#xff08;带偏移量&#xff09;-- 跳过前10条&#xff0c;返回接下来的5条记录&#xff08;第11-15条&#xff09; SELECT * FROM products LIMIT 10, 5;-- MySQL 8.0 也支持这种语法 S…

maven 发布到中央仓库之持续集成-03

maven 系列 maven-01-发布到中央仓库概览 maven-02-发布到中央仓库常用脚本 maven-03-发布到中央仓库之持续集成 maven-04-发布到中央仓库之 Ignore Licence maven-05-maven 配置进阶学习 maven-06-maven 中央仓库 OSSRH 停止服务&#xff0c;Central Publishing Portal …

(补充)RS422

RS4221. 基本定义与定位 官方名称&#xff1a; EIA/TIA-422&#xff08;电子工业协会/电信工业协会标准422&#xff09;。类型&#xff1a; 一种定义了电气特性的 平衡式差分 串行通信标准。目的&#xff1a; 克服 RS-232 在传输距离、速率和抗干扰能力上的严重局限性。核心思想…

自建ELK vs 云商日志服务:成本对比分析

在当今数据驱动的时代&#xff0c;日志管理已成为企业IT基础设施中不可或缺的一部分。面对日益增长的日志数据&#xff0c;许多团队都在纠结&#xff1a;是自建ELK&#xff08;Elasticsearch、Logstash、Kibana&#xff09;堆栈&#xff0c;还是直接使用云服务商提供的日志服务…

Eigen 几何模块深拆:Isometry3d vs Affine3d + 变换矩阵本质详解

文章目录0 写在前面1 数学背景对比2 Eigen 实现差异3 Isometry3d 是不是 4 4 矩阵&#xff1f;4 核心 API 速查5 实战示例5.1 SLAM 位姿链&#xff1a;相机点 → 世界点5.2 体素滤波&#xff1a;各向异性缩放&#xff08;X/Y → 5 cm&#xff0c;Z → 10 cm&#xff09;5.3 把…

python的病例管理系统

前端开发框架:vue.js 数据库 mysql 版本不限 后端语言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 数据库工具&#xff1a;Navicat/SQLyog等都可以 随着医疗…

博客系统开发全流程解析(前端+后端+数据库)与 AI 协作初体验

一、前言&#xff1a;为什么选择博客系统作为全栈入门&#xff1f; 对于初入编程世界的开发者来说&#xff0c;“全栈” 似乎是一个庞大而遥远的概念。前端、后端、数据库、部署运维… 知识体系繁杂&#xff0c;令人望而生畏。选择一个目标明确、功能完整且贴近实际应用的项目…

Xavier公式的原理

数学原理&#xff1a; (1) 前向传播的方差一致性 假设输入 x 的均值为 0&#xff0c;方差为 σx2σ_x^2σx2​&#xff0c;权重 W的均值为 0&#xff0c;方差为 σW2σ_W^2σW2​&#xff0c;则输出 zWxzWxzWx的方差为&#xff1a; Var(z)nin⋅Var(W)⋅Var(x) Var(z)n_{in}⋅Va…

pytorch学习笔记(二)-- pytorch模型开发步骤详解

简介&#xff1a; 本章主要是针对Pytorch神经网络的开发步骤做一个详细的总结&#xff0c;对每一步的前世今生做一个了解&#xff0c;下面先列一下开发需要的步骤有哪些&#xff1a; 模型构建&#xff0c;主要是前向传递函数的确认确认损失函数以及学习步频&#xff08;learni…

consul 的安装与服务发现

1. helm 安装 consul 到 k8s 安装放在这里了&#xff1a;https://github.com/lianan2/installation/tree/master/consul-helm consul 的常用命令&#xff1a; # 查看集群状态 kubectl -n consul exec -it consul-server-0 -- consul operator raft list-peers kubectl -n con…

ros topic和service的使用

在做ldiar slam的时候&#xff0c;最常用的当属topic&#xff0c;偶尔也会用一下service&#xff0c;action则很少使用。现在一块来看一下topic的使用。一、topic的使用topic的消息订阅和发布#include<ros/ros.h> #include<rosbag/bag.h> #include<rosbag/view.…