Electron (02)集成 SpringBoot:服務與桌面程序協同啟動方案

本篇是關于把springboot生成的jar打到electron里,在生成的桌面程序啟動時springboot服務就會自動啟動。

雖然之后并不需要這種方案,更好的是部署[一套服務端,多個客戶端]...但是既然搭建成功了,也記錄一下。

前端文件

1、main.js

const { app, BrowserWindow, ipcMain, Notification, Menu,dialog} = require('electron/main')
const path = require('node:path')
const childProcess = require('child_process');
const fs = require('fs')let win = null;       // Electron主窗口實例
let backendProcess = null;   // Java子進程實例
const BACKEND_PORT = 8080;   // 后端固定端口(可配置)
const JAR_FILENAME = 'helloworld-0.0.1-SNAPSHOT.jar'; // JAR文件名(需與resources目錄下的文件一致)function writeFile(_, data) {fs.writeFileSync('D:/hello.txt', data)
}function readFile() {const res = fs.readFileSync('D:/hello.txt').toString();return res
}/*** 獲取JAR包路徑(兼容開發/生產環境)*/
function getJarPath() {if (app.isPackaged) {// 生產環境:打包后,資源目錄為process.resourcesPathreturn path.join(process.resourcesPath, 'resources', JAR_FILENAME);} else {// 開發環境:資源目錄為項目根目錄的`resources`文件夾return path.join(__dirname, 'resources', JAR_FILENAME);}
}/*** 4. 啟動Java子進程(核心邏輯)*/
function startBackend() {const jarPath = getJarPath();// 檢查JAR包是否存在(避免啟動失敗)if (!fs.existsSync(jarPath)) {dialog.showErrorBox('錯誤', `JAR包不存在:${jarPath}`);app.quit();return;}// 構造Java啟動參數(可添加Spring Boot配置,如端口、環境)const args = ['-jar',jarPath,`--server.port=${BACKEND_PORT}`,          // 指定后端端口(避免沖突)`--spring.profiles.active=prod`           // 指定生產環境配置(可選)];// 構造子進程選項(跨平臺優化)const options = {windowsHide: true,  // Windows下隱藏命令行窗口(避免彈出黑框)env: { ...process.env }, // 傳遞環境變量cwd: path.dirname(jarPath) // 設置子進程工作目錄(避免相對路徑問題)};// 啟動子進程(使用spawn,適合長時間運行的進程)backendProcess = childProcess.spawn('java', args, options);// 5. 監聽后端輸出(調試用)backendProcess.stdout.on('data', (data) => {console.log('[Backend]', data.toString().trim());});// 6. 監聽后端錯誤(如Java未安裝、端口沖突)backendProcess.stderr.on('data', (data) => {const errorMsg = data.toString().trim();console.error('[Backend Error]', errorMsg);// 處理端口沖突(示例)if (errorMsg.includes(`Port ${BACKEND_PORT} is already in use`)) {dialog.showErrorBox('錯誤', `后端端口${BACKEND_PORT}已被占用,請關閉占用程序后重試。`);app.quit();}});// 7. 后端退出事件(如異常崩潰)backendProcess.on('exit', (code) => {console.log('[Backend]', `進程退出,代碼:${code}`);backendProcess = null;// 若后端異常退出,關閉Electron應用if (code !== 0 && app.isReady()) {dialog.showErrorBox('錯誤', '后端進程異常退出,請重啟應用。');app.quit();}});
}function createWindow() {const win = new BrowserWindow({width: 1000,height: 800,title: '簡單網頁',webPreferences: {preload: path.join(__dirname, 'preload.js')}})ipcMain.on('file-save', writeFile)ipcMain.handle('file-read', readFile)// 加載前端頁面(兼容開發/生產環境)if (app.isPackaged) {console.log('pro')} else {console.log('dev')}//自定義菜單項let menuTemp = [{label: '文件',submenu: [{label: '打開文件',click() {console.log('打開一個具體的文件')}},{ label: '打開文件夾' },{label: '關于',role: 'about'}]},{ label: '編輯' }]//生成自定義菜單let menu = Menu.buildFromTemplate(menuTemp)Menu.setApplicationMenu(menu)win.loadFile('index.html')// 創建并顯示通知const notification = new Notification({title: '主進程通知',body: '恭喜你,學會了求雨之術,風來~雨來~'}).show();// 確保在窗口創建后調用 openDevToolswin.webContents.on('did-finish-load', () => {win.webContents.openDevTools();});// 定時發送時間給渲染進程(每1秒)setInterval(() => {if (win && !win.isDestroyed()) {win.webContents.send('main-time', new Date().toLocaleTimeString());}}, 1000);
}
app.whenReady().then(() => {startBackend();       // 啟動后端(先啟動后端,再創建窗口)createWindow();   // 創建主窗口})// 應用退出前確保后端進程終止
app.on('will-quit', () => {if (backendProcess) backendProcess.kill();
}); 

2、index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="Content-Security-Policy" content="default-src 'self'; connect-src 'self' http://localhost:8080;">
<link rel="stylesheet" href="styles.css">
</head><body><div id="time">當前時間:加載中...</div><div class="hint">注意:輸入內容,可以保存到d:/hello.txt,點擊讀取,可以讀取該文件內容</div><input id="input" type="text"><button id="btn2">向D盤輸入hello.txt</button><br><br><hr><button id="btn3">讀取D盤hello.txt</button><br><br><hr><button id="sendRequest">點擊發送請求</button><div id="result"></div><script type="text/javascript" src="./render.js"></script>
</body></html>

3、render.js

const timeElement = document.getElementById('time');
const btn2 = document.getElementById('btn2');
const btn3 = document.getElementById('btn3');
const btn4 = document.getElementById('sendRequest');
const resultDiv = document.getElementById('result');
const input = document.getElementById('input');btn2.onclick = () => {myAPI.saveFile(input.value)
}btn3.onclick = async () => {let data = await myAPI.readFile()alert(data)
}// 定義常量
const API_URL = 'http://localhost:8080/getcode';
const METHOD = 'GET';// 綁定按鈕點擊事件
btn4.onclick = async () => {try {// 發送 GET 請求const response = await fetch(API_URL, { method: METHOD });// 檢查響應狀態if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}// 解析字符串數據const data = await response.text(); // 使用 text() 方法解析字符串// 將數據回顯到頁面上resultDiv.innerHTML = `<p class="success">請求成功!<br>返回數據:</p><pre>${data}</pre>`;} catch (error) {resultDiv.innerHTML = `<p class="error">請求失敗,請檢查網絡或后端服務是否正常運行!</p>`;}
};// 監聽主進程發送的時間消息
myAPI.onMainTime((time) => {timeElement.textContent = `當前時間:${time}`;
});

4、preload.js

const { contextBridge, ipcRenderer } = require('electron')contextBridge.exposeInMainWorld('myAPI', {saveFile: (data) => {ipcRenderer.send('file-save', data)},readFile: () => {return ipcRenderer.invoke('file-read')},// 監聽主進程發送的時間消息onMainTime: (callback) => {ipcRenderer.on('main-time', (event, time) => callback(time))}
})

5、package.json

  "scripts": {"start": "electron .","build": "electron-builder --win --x64","package": "electron-packager . construction --win --out build --arch=x64 --version1.0.0 --overwrite --icon=static/images/128.ico","make": "electron-forge make"},"build": {"appId": "com.xiaoyumao.demo","extraResources": {"from": "resources/helloworld-0.0.1-SNAPSHOT.jar","to": "resources/helloworld-0.0.1-SNAPSHOT.jar"},"win": {"target": [{"target": "nsis","arch": ["x64"]}]},"nsis": {"oneClick": false,"perMachine": true,"allowToChangeInstallationDirectory": true}},

Electron中集成jar

1、先得有jar包

使用springboot技術,快速生成一個web應用。寫一個getcode接口,

    @GetMapping("/getcode")public String getcode(){UUID randomUUID = UUID.randomUUID();String uuidWithoutHyphens = randomUUID.toString().replace("-", "");return "隨機編碼:"+uuidWithoutHyphens;}

在瀏覽器測試的訪問一下

沒啥問題后,用maven進行打包,生成可以獨立運行的jar


2、child_process啟動jar

由Electron主進程(Node環境)創建的獨立進程,來啟動jar

child_process.spawn()

用于創建一個子進程并實時監聽其輸入和輸出。

java -jar C:\Users\lenovo\electron-basics\resources\helloworld-0.0.1-SNAPSHOT.jar --server.port=8080

3、resource目錄

還需要在package.json配置extraResources?,用于在構建 Electron 應用程序時將額外的資源文件打包到最終的應用程序安裝包中。它的主要作用是確保應用程序所需的資源文件能夠正確地隨應用一起發布,而不會丟失。

    "extraResources": {"from": "resources/helloworld-0.0.1-SNAPSHOT.jar","to": "resources/helloworld-0.0.1-SNAPSHOT.jar"},

在 Electron 中,process.resourcesPath?指向的是應用程序的資源目錄。

    if (app.isPackaged) {// 生產環境:打包后,資源目錄為process.resourcesPathreturn path.join(process.resourcesPath, 'resources', JAR_FILENAME);} 

在這里資源文件都放在了electron本身生成的resources目錄中


4、假如沒有JAVA_HOME環境

有些情況就是,電腦它沒有javahome環境,或者有但是配置的不是我們想要的jdk1.8。。

所以我決定打包的時候把jre環境也打進去,jar啟動原理就是下面這樣的

C:\Users\lenovo\electron-basics\resources\jre\bin\java -jar C:\Users\lenovo\electron-basics\resources\helloworld-0.0.1-SNAPSHOT.jar --server.port=8080

在resource目錄下把jre環境放進去。

package.json就得改變了

    "extraResources": [{"from": "resources/jre","to": "resources/jre"},{"from": "resources/helloworld-0.0.1-SNAPSHOT.jar","to": "resources/helloworld-0.0.1-SNAPSHOT.jar"}],

在main.js中,關于啟動jar包的命令、對java環境的檢查等都要用xxx\jre\bin\java去檢查

在安裝軟件后,目錄是這樣的

現在就算沒有JAVA_HOME,也照樣可以運行


JSP應用

如果項目之前是用jsp寫的,那么能不能啥都不改的情況下,直接訪問l

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/910299.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/910299.shtml
英文地址,請注明出處:http://en.pswp.cn/news/910299.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2025年計算機應用與神經網絡國際會議(CANN 2025)

2025 International Conference on Computer Applications and Neural Networks &#xff08;一&#xff09;會議信息 會議簡稱&#xff1a;CANN 2025 大會地點&#xff1a;中國重慶 收錄檢索&#xff1a;提交Ei Compendex,CPCI,CNKI,Google Scholar等 &#xff08;二&#x…

振動分析中的低頻噪聲問題:從理論到實踐的完整解決方案

前言 在振動監測和結構健康監測領域&#xff0c;我們經常需要從加速度信號計算速度和位移。然而&#xff0c;許多工程師在實際應用中都會遇到一個令人困擾的問題&#xff1a;通過積分計算得到的速度和位移頻譜中低頻噪聲異常放大。 本文將深入分析這個問題的根本原因&#xf…

ncu學習筆記01——合并訪存

全局內存通過緩存實現加載和存儲過程。其中&#xff0c;L1為一級緩存&#xff0c;每個SM都有自己的L1&#xff1b;L2為二級緩存&#xff0c;L2則被所有SM共有。 數據從全局內存到SM的傳輸過程中&#xff0c;會去L1和L2中查詢是否有緩存。對全局內存的訪問將經過L1&#xff1b;…

2012 - 正方形矩陣

????題目描述 晶晶同學非常喜歡方形&#xff0c;她希望打印出來的字符串也是方形的。老師給了晶晶同學一個字符串"ACM"&#xff0c;晶晶同學突發奇想&#xff0c;如果任意給定義一個整數n&#xff0c;能不能打印出由這個字符串組成的正方形字符串呢&#xff1f;…

C++中set的常見用法

在 C 里&#xff0c;std::set屬于標準庫容器的一種&#xff0c;其特性是按照特定順序存儲唯一的元素。下面為你詳細介紹它的常見使用方法&#xff1a; 1. 頭文件引入 要使用std::set&#xff0c;需要在代碼中包含相應的頭文件&#xff1a; #include <set> 2. 集合的定…

stm32移植freemodbus

1、設置串口 開啟串口中斷 2、設置定時器 已知在freemodbus中默認定義&#xff1a;當波特率大于19200時&#xff0c;判斷一幀數據超時時間固定為1750us&#xff0c;當波特率小于19200時&#xff0c;超時時間為3.5個字符時間。這里移植的是115200&#xff0c;所以一幀數據超時…

鴻蒙next 使用canvas實現ecg動態波形繪制

該代碼可在Arkts 與 前端使用&#xff0c;基于canvas 倉庫地址&#xff1a;https://gitee.com/harmony_os_example/harmony-os-ecg-waveform.git 代碼中的list數組為波形數據&#xff0c;該示例需要根據自己業務替換繪制頻率&#xff0c;波形數據&#xff0c;ecg原始數據生成…

基于原生能力的鍵盤控制

基于原生能力的鍵盤控制 前言一、進入頁面TextInput獲焦1、方案2、核心代碼 二、點擊按鈕或其他事件觸發TextInput獲焦1、方案2、核心代碼 三、鍵盤彈出后只上抬特定的輸入組件1、方案2、核心代碼 四、監聽鍵盤高度1、方案2、核心代碼 五、設置窗口在鍵盤抬起時的頁面避讓模式為…

大數據治理域——數據存儲與成本管理

摘要 本文主要探討了數據存儲與成本管理的多種策略。介紹了數據壓縮技術&#xff0c;如MaxCompute的archive壓縮方法&#xff0c;通過RAID file形式存儲數據&#xff0c;可有效節省空間&#xff0c;但恢復時間較長&#xff0c;適用于冷備與日志數據。還詳細闡述了數據生命周期…

國產Linux銀河麒麟操作系統上使用自帶openssh遠程工具SSH方式登陸華為交換機或服務器

在Windows和Linux Debian系統上我一直使用electerm遠程工具訪問服務器或交換機&#xff0c; 一、 electerm簡介 簡介&#xff1a;electerm是一款開源免費的SSH工具&#xff0c;具有良好的跨平臺兼容性&#xff0c;適用于Windows、macOS、Linux以及麒麟操作系統。特點&#xf…

Logback 在java中的使用

Logback 是 Java 應用中廣泛使用的日志框架&#xff0c;以下是其核心使用方法及最佳實踐&#xff1a; 1. 引入依賴 在 Maven 或 Gradle 項目中添加 Logback 及 SLF4J 依賴&#xff1a; <!-- Maven --> <dependency><groupId>ch.qos.logback</groupId>…

Axure應用交互設計:中繼器—整行、條件行、當前行賦值

親愛的小伙伴,如有幫助請訂閱專欄!跟著老師每課一練,系統學習Axure交互設計課程! Axure產品經理精品視頻課https://edu.csdn.net/course/detail/40420 課程主題:對中繼器中:整行、符合某種條件的任意行、當前行的賦值操作 課程視頻:

ToolsSet之:TTS及Morse編解碼

ToolsSet是微軟商店中的一款包含數十種實用工具數百種細分功能的工具集合應用&#xff0c;應用基本功能介紹可以查看以下文章&#xff1a; Windows應用ToolsSet介紹https://blog.csdn.net/BinField/article/details/145898264其中Text菜單中的TTS & Morse可用于將文本轉換…

【C++】編碼傳輸:創建零拷貝幀對象4:shared_ptr轉unique_ptr給到rtp打包

【C++】編碼傳輸:創建零拷貝幀對象3: dll api轉換內部的共享內存根本原因 你想要的是基于 packet 指向的那個已有對象,拷貝(或移動)出一個新的 VideoDataPacket3 實例,因此需要把那個對象本身傳進去——也就是 *packet。copilot的原因分析與gpt一致 The issue is with t…

基于UDP的套接字通信

udp是一個面向無連接的&#xff0c;不安全的&#xff0c;報式傳輸層協議&#xff0c;udp的通信過程默認也是阻塞的。使用UDP進行通信&#xff0c;服務器和客戶端的處理步驟比TCP要簡單很多&#xff0c;并且兩端是對等的 &#xff08;通信的處理流程幾乎是一樣的&#xff09;&am…

華為CE交換機抓包

capture-packet interface 100GE1/0/5 destination file 001.cap packet-len 64 注&#xff1a;早期版本&#xff08;disp device&#xff09;可能在系統視圖下&#xff08;sys&#xff09; 抓完包后可以看到對應文件&#xff08;早期版本在根目錄下&#xff09;&#xff1a;…

Python 數據分析與可視化 Day 3 - Pandas 數據篩選與排序操作

&#x1f3af; 今日目標 掌握 DataFrame 的條件篩選&#xff08;布爾索引&#xff09;學會多條件篩選、邏輯運算熟練使用排序&#xff08;sort_values&#xff09;提升數據組織力結合列選擇進行數據提取分析 &#x1f9ea; 一、列選擇與基本篩選 ? 選擇單列 / 多列 df[&quo…

Vite項目初始化與配置

下面,我們來系統的梳理關于 Vite 項目初始化與配置 的基本知識點: 一、Vite 核心概念與優勢 1.1 什么是 Vite? Vite(法語意為 “快速”)是新一代的前端構建工具,由 Vue.js 作者尤雨溪開發。它解決了傳統構建工具(如 Webpack)在開發環境中的性能瓶頸問題。 1.2 Vite …

Transformer中的核心問題 知識點匯總

Transformer架構圖 transformer整體架構 1. Transformer 的參數配置 Transformer 的Encoder層和Decoder層都使用6個注意力模塊&#xff0c;所有的子網絡的輸出維度均為512維&#xff0c;多頭注意力部分使用了8個注意力頭。 2. 歸一化的方式 歸一化的方式為LayerNorm&#xff0c…

python web開發-Flask數據庫集成

Flask 數據庫集成完全指南&#xff1a;Flask-SQLAlchemy 實踐 1. 引言 數據庫是現代Web應用的核心組件&#xff0c;Flask通過Flask-SQLAlchemy擴展提供了強大的數據庫集成能力。本文將全面介紹如何在Flask應用中使用Flask-SQLAlchemy進行數據庫操作&#xff0c;涵蓋從基礎配置…