一份使用Electron + Spring Boot构建桌面应用程序的指南

前言

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

1. 创建Gradle项目

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

gradle init --type java-application

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

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

plugins {
    id 'java'
}

group 'cn.gsein'
version '1.0'

repositories {
    mavenCentral()
}

dependencies {
}

2. 在项目中引入Spring Boot

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

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

在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);
    }
}

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

3. 在项目中引入Vue和Electron

3.1 安装Node.js和Npm

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

3.1 安装vue-cli3

npm install @vue/cli -g

3.2 创建vue项目

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

vue create electron-vue-demo

3.3 安装electron

进入到项目根目录,执行

vue add electron-builder

3.4 修改项目配置

在项目根目录下创建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'));
  }
};

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

function createWindow () {
      // Create the browser window.
      win = new BrowserWindow({
        width: 1200,
        height: 620,
        webPreferences: {
+         webSecurity: false,
          nodeIntegration: true
        }
      })

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

4. Electron和Spring Boot联通性调试

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

5. 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'
}
  • 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。

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

6.1 启动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")
      })
    }))
  }
}

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

6.2 保证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()
})

6.3 保证所有窗口关闭后关闭掉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()
    })
  }

})

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

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

7.1 将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)
    }

7.2 将启动脚本中的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"

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

tasks {
    bootStartScripts {
        (unixStartScriptGenerator as TemplateBasedScriptGenerator).template = resources.text.fromFile("customUnixStartScript.txt")
        (windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = resources.text.fromFile("customWindowsStartScript.txt")
    }
}

8. 优缺点

优点:熟悉的技术栈
缺点:应用启动较慢,应用的安装包较大

附录1 参考项目

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

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

附录2 相关技术简介

Gradle

Electron

Vue

Spring Boot

参考资料

  • Java Electron Tutorial
  • 手把手教你使用Electron5+vue-cli3开发跨平台桌面应用

你可能感兴趣的:(一份使用Electron + Spring Boot构建桌面应用程序的指南)