吉森的技术小站 吉森的技术小站
首页
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

吉森

Fuel your ambition
首页
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java基础

  • Spring框架

    • Electron + Spring Boot 桌面应用开发指南
      • 前言
      • 创建Gradle项目
      • 在项目中引入Spring Boot
      • 在项目中引入Vue和Electron
        • 安装Node.js和Npm
        • 安装vue-cli3
        • 创建vue项目
        • 安装electron
        • 修改项目配置
      • Electron和Spring Boot联通性调试
      • Gradle中引入Node、Spring Boot和Application插件
      • 技术细节:怎么实现Electron和Spring Boot进程同时启停?
        • 启动Electron同时启动Spring Boot
        • 保证Spring Boot进程启动后再打开窗口
        • 保证所有窗口关闭后关闭掉Spring Boot进程
      • 技术细节:怎么在构建的安装包中内置Jre?
        • 将Jre拷贝到Electron模块的public目录下
        • 将启动脚本中的JAVACMD修改为Jre中的java命令
      • 优缺点
      • 附录
        • 参考项目
        • 相关技术简介
      • 参考资料
    • Spring Boot菜谱(一)——编写自动记录Controller层请求的注解
  • 第三方库

  • Java
  • Spring框架
吉森
2021-03-17
目录

Electron + Spring Boot 桌面应用开发指南

ElectronSpring BootVueGradle 0 人阅读

# 前言

在这份指南中,我将向大家分享如何使用Electron + Spring Boot这样的组合来构建桌面应用程序。除了上述两种技术外,我们还会使用到Vue和Gradle这样的技术。

# 创建Gradle项目

可以使用Intellij IDEA等IDE进行创建,也可以在命令行中使用如下命令:

gradle init --type java-application
1

如果使用命令行的方式,需要先下载最新版本的Gradle (https://gradle.org/releases/)并配置好环境变量。

新创建好的项目中包含了build.gradle文件。现在让我们来修改这个文件:

plugins {
    id 'java'
}

group 'cn.gsein'
version '1.0'

repositories {
    mavenCentral()
}

dependencies {
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 在项目中引入Spring Boot

在build.gradle的dependencies中加入Spring Boot相关依赖的坐标:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web:2.4.2'
}
1
2
3

在IDE中刷新Gradle工程,这样就可以看到Spring Boot相关的依赖包被引入项目中。

接下来需要创建Spring Boot的启动类,在src/main/java目录下新建项目的包,如cn.gsein.demo,在新建的包中创建Application启动类:

package cn.gsein.demo;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;

/**
 * @author G. Seinfeld
 * @since 2021/03/17
 */
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        new SpringApplicationBuilder(Application.class).run(args);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

从IDE中启动项目,如果没有问题的话,在浏览器中通过http://localhost:8080访问项目,会提示"Whitelabel Error Page"。

# 在项目中引入Vue和Electron

# 安装Node.js和Npm

在开始之前,需要先安装最新版Node.js和Npm

# 安装vue-cli3

npm install @vue/cli -g
1

# 创建vue项目

在src/main目录下,执行以下命令,创建vue项目:

vue create electron-vue-demo
1

# 安装electron

进入到项目根目录,执行

vue add electron-builder
1

# 修改项目配置

在项目根目录下创建vue.config.js,粘贴以下代码:

const path = require('path');

function resolve (dir) {
  return path.join(__dirname, dir);
}

module.exports = {
  publicPath: './',
  devServer: {
    // can be overwritten by process.env.HOST
    host: '0.0.0.0',  
    port: 8080
  },
  chainWebpack: config => {
    config.resolve.alias
      .set('@', resolve('src'))
      .set('src', resolve('src'))
      .set('common', resolve('src/common'))
      .set('components', resolve('src/components'));
  }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

为了取消跨域限制,在background.js中创建窗口时做如下修改:

function createWindow () {
      // Create the browser window.
      win = new BrowserWindow({
        width: 1200,
        height: 620,
        webPreferences: {
          webSecurity: false,
          nodeIntegration: true
        }
      })
1
2
3
4
5
6
7
8
9
10

更多细节可以参考https://zhuanlan.zhihu.com/p/75764907。

# Electron和Spring Boot联通性调试

可以在js中增加Electron向Spring Boot发送Http请求的接口,然后IDE中分别启动Electron和Spring Boot进程,测试是否能够正常发送请求。如果发生跨域的问题,请参考第3部分修改配置。

# Gradle中引入Node、Spring Boot和Application插件

plugins {
    id 'java'
    id 'org.springframework.boot' version "2.4.2"
    id 'com.moowork.node' version "1.3.1"
    id 'application'
}
1
2
3
4
5
6
  • Node插件:提供了nodeSetup、npmSetup、npmInstall等任务,可以用于管理Node.js项目,方便将Electron和Spring Boot进行统一管理。
  • Spring Boot插件:用于构造Spring Boot应用
  • Application插件:用于应用的构造、部署、发布等,还可以用于生成运行java应用的shell或bat启动脚本

当项目中同时存在Spring Boot和Application插件时,Application插件的所有任务将转变为Spring boot版本的任务,如任务startScripts(生成启动脚本)转变为bootStartScripts。

# 技术细节:怎么实现Electron和Spring Boot进程同时启停?

# 启动Electron同时启动Spring Boot

可以使用Node.js的child_process来实现,如下:

let serverProcess
if (isDevelopment) {
  serverProcess = true
} else {
  if (platform === 'win32') {
    serverProcess = require('child_process').spawn('cmd.exe', ['/c', 'redis-client.bat'], {
      cwd: app.getAppPath() + '/bin'
    })
  } else {
    const chmod = require('child_process').spawn('chmod', ['+x', app.getAppPath() + "/bin/redis-client"]);
    chmod.on('close', (code => {
      const chmod2 = require('child_process').spawn('chmod', ['+x', app.getAppPath() + "/runtime/bin/java"]);
      chmod2.on('close', () => {
        serverProcess = require('child_process').spawn(app.getAppPath() + "/bin/redis-client")
      })
    }))
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

也就是说,在Electron启动后,利用Node.js的child_process去执行在构建环节中生成的执行脚本。

# 保证Spring Boot进程启动后再打开窗口

我们可以利用Node.js的第三方依赖包minimal-request-promise来检查Spring Boot进程是否已经成功启动。minimal-request-promise可以用来发送http请求,它的体积很小,基本没有其他依赖。我们可以向Spring Boot端一个有效的Url发送请求,如果请求成功,证明进程已启动,可以打开窗口;否则,隔一段时间再次发送请求。

const startUp = function () {
  const requestPromise = require('minimal-request-promise')
  requestPromise.get(appUrl).then(function (response) {
    console.log(response);
    console.log('Server started!');
    createWindow();
    appStarted = true
  }, function (response) {
    console.log(response)
    console.log('Waiting for the server start...');
    setTimeout(function () {
      startUp()
    }, 500)
  })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
  startUp()
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 保证所有窗口关闭后关闭掉Spring Boot进程

可以使用Node.js的第三方依赖包tree-kill,将Spring Boot端的进程杀掉。

// Quit when all windows are closed.
app.on('window-all-closed', (e) => {
  if (serverProcess && process.platform !== 'darwin') {
    e.preventDefault()
    const kill = require('tree-kill')
    kill(serverProcess.pid, 'SIGTERM', function () {
      console.log('Server process killed')
      serverProcess = null
      app.quit()
    })
  }

})
1
2
3
4
5
6
7
8
9
10
11
12
13

# 技术细节:怎么在构建的安装包中内置Jre?

为了在没有安装java(或安装版本不符合要求)的机器上运行应用,需要在应用中内置Java运行环境(Jre)。

# 将Jre拷贝到Electron模块的public目录下

可以使用Gradle task中的copy函数进行操作

var targetDir = project.file("src/main/electron/redis-electron/public")

var runtimeDir = File(targetDir, "runtime")
if (runtimeDir.exists()) {
    runtimeDir.delete()
}
runtimeDir.mkdir()

copy {
    from(File(System.getProperty("java.home"), "jre"))
    into(runtimeDir)
}
1
2
3
4
5
6
7
8
9
10
11
12

# 将启动脚本中的JAVACMD修改为Jre中的java命令

Gradle application插件生成的启动脚本默认使用环境变量中配置的java命令,为了使用自定义的命令位置,需要修改启动脚本。

这里我们可以对Gradle application插件进行配置,使用自定义的模板来生成启动脚本。由于我们同时使用了Spring Boot插件,这里需要更改的是Spring Boot插件中的bootStartScripts任务,将任务中unix和windows脚本生成器的模板设定为自定义的模板。

自定义模板的内容可以照抄官方模板,只把涉及到JAVACMD的部分进行修改即可。这里我们以unix版的脚本为例,将其中涉及JAVACMD判断的部分修改为:

# Determine the Java command to use to start the JVM.
JAVACMD="\$APP_HOME/runtime/bin/java"
1
2

完整的模板可以参考附录中的参考项目

tasks {
    bootStartScripts {
        (unixStartScriptGenerator as TemplateBasedScriptGenerator).template = resources.text.fromFile("customUnixStartScript.txt")
        (windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = resources.text.fromFile("customWindowsStartScript.txt")
    }
}
1
2
3
4
5
6

# 优缺点

优点:

  • 熟悉的技术栈

缺点:

  • 应用启动较慢
  • 应用的安装包较大

# 附录

# 参考项目

基于本文技术开发的Redis桌面连接工具:

  • GitHub地址:https://github.com/lhing17/gsein-redis-client.git
  • Gitee地址:https://gitee.com/lhing17/gsein-redis-client.git

# 相关技术简介

  • Gradle:现代化的构建工具 (https://gradle.org)
  • Electron:使用Web技术构建跨平台桌面应用 (https://www.electronjs.org)
  • Vue:渐进式JavaScript框架 (https://vuejs.org)
  • Spring Boot:简化Spring应用开发的框架 (https://spring.io/projects/spring-boot)

# 参考资料

  • Java Electron Tutorial (opens new window)
  • 手把手教你使用Electron5+vue-cli3开发跨平台桌面应用 (opens new window)
编辑 (opens new window)
#Electron#Spring Boot#Vue#Gradle
上次更新: 2025/08/08, 17:43:26
Java Stream findFirst方法的空指针陷阱详解
Spring Boot菜谱(一)——编写自动记录Controller层请求的注解

← Java Stream findFirst方法的空指针陷阱详解 Spring Boot菜谱(一)——编写自动记录Controller层请求的注解→

最近更新
01
怎么写好技术文章?
08-25
02
CommonJS与ES模块:新手完全指南
08-21
03
Java Stream findFirst方法的空指针陷阱详解
08-14
更多文章>
Theme by Vdoing | Copyright © 2024-2025 吉森 | MIT License | 吉ICP备17006653号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式